Sanity check ICMP6 router advertisement packets
am: a36213867a -s ours
Change-Id: I8371809758391158effb378fae4b6fc9aaca553c
diff --git a/Android.mk b/Android.mk
index 49825b9..9dc4aef 100644
--- a/Android.mk
+++ b/Android.mk
@@ -75,6 +75,7 @@
core/java/android/app/IAppTask.aidl \
core/java/android/app/ITaskStackListener.aidl \
core/java/android/app/IBackupAgent.aidl \
+ core/java/android/app/IEphemeralResolver.aidl \
core/java/android/app/IInstrumentationWatcher.aidl \
core/java/android/app/INotificationManager.aidl \
core/java/android/app/IProcessObserver.aidl \
@@ -305,9 +306,9 @@
core/java/com/android/internal/app/IAppOpsService.aidl \
core/java/com/android/internal/app/IAssistScreenshotReceiver.aidl \
core/java/com/android/internal/app/IBatteryStats.aidl \
- core/java/com/android/internal/app/IEphemeralResolver.aidl \
core/java/com/android/internal/app/ISoundTriggerService.aidl \
core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl \
+ core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl \
core/java/com/android/internal/app/IVoiceInteractionSessionShowCallback.aidl \
core/java/com/android/internal/app/IVoiceInteractor.aidl \
core/java/com/android/internal/app/IVoiceInteractorCallback.aidl \
@@ -318,6 +319,7 @@
core/java/com/android/internal/appwidget/IAppWidgetHost.aidl \
core/java/com/android/internal/backup/IBackupTransport.aidl \
core/java/com/android/internal/backup/IObbBackupService.aidl \
+ core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl \
core/java/com/android/internal/policy/IKeyguardDrawnCallback.aidl \
core/java/com/android/internal/policy/IKeyguardExitCallback.aidl \
core/java/com/android/internal/policy/IKeyguardService.aidl \
@@ -341,6 +343,7 @@
core/java/com/android/internal/view/IInputMethodManager.aidl \
core/java/com/android/internal/view/IInputMethodSession.aidl \
core/java/com/android/internal/view/IInputSessionCallback.aidl \
+ core/java/com/android/internal/widget/ICheckCredentialProgressCallback.aidl \
core/java/com/android/internal/widget/ILockSettings.aidl \
core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \
core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \
@@ -467,6 +470,9 @@
../../system/update_engine/binder_bindings/android/os/IUpdateEngine.aidl \
../../system/update_engine/binder_bindings/android/os/IUpdateEngineCallback.aidl \
+LOCAL_SRC_FILES += \
+ ../../system/netd/server/binder/android/net/INetd.aidl \
+
LOCAL_AIDL_INCLUDES += system/update_engine/binder_bindings
# FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk
@@ -699,6 +705,7 @@
frameworks/base/core/java/android/service/quicksettings/Tile.aidl \
frameworks/native/aidl/binder/android/os/PersistableBundle.aidl \
system/netd/server/binder/android/net/UidRange.aidl \
+ frameworks/base/telephony/java/android/telephony/PcoData.aidl \
gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/framework.aidl
$(gen): PRIVATE_SRC_FILES := $(aidl_files)
diff --git a/api/current.txt b/api/current.txt
index 4f12ad4..533b577 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -397,6 +397,7 @@
field public static final int colorPressedHighlight = 16843661; // 0x101038d
field public static final int colorPrimary = 16843827; // 0x1010433
field public static final int colorPrimaryDark = 16843828; // 0x1010434
+ field public static final int colorSecondary = 16844080; // 0x1010530
field public static final int columnCount = 16843639; // 0x1010377
field public static final int columnDelay = 16843215; // 0x10101cf
field public static final int columnOrderPreserved = 16843640; // 0x1010378
@@ -420,7 +421,9 @@
field public static final int contentInsetStart = 16843859; // 0x1010453
field public static final int contentInsetStartWithNavigation = 16844066; // 0x1010522
field public static final int contextClickable = 16844007; // 0x10104e7
+ field public static final int contextDescription = 16844078; // 0x101052e
field public static final int contextPopupMenuStyle = 16844033; // 0x1010501
+ field public static final int contextUri = 16844077; // 0x101052d
field public static final int controlX1 = 16843772; // 0x10103fc
field public static final int controlX2 = 16843774; // 0x10103fe
field public static final int controlY1 = 16843773; // 0x10103fd
@@ -1039,6 +1042,7 @@
field public static final int rotation = 16843558; // 0x1010326
field public static final int rotationX = 16843559; // 0x1010327
field public static final int rotationY = 16843560; // 0x1010328
+ field public static final int roundIcon = 16844076; // 0x101052c
field public static final int rowCount = 16843637; // 0x1010375
field public static final int rowDelay = 16843216; // 0x10101d0
field public static final int rowEdgeFlags = 16843329; // 0x1010241
@@ -1106,11 +1110,16 @@
field public static final int shareInterpolator = 16843195; // 0x10101bb
field public static final int sharedUserId = 16842763; // 0x101000b
field public static final int sharedUserLabel = 16843361; // 0x1010261
+ field public static final int shortcutDisabledMessage = 16844075; // 0x101052b
+ field public static final int shortcutId = 16844072; // 0x1010528
+ field public static final int shortcutLongLabel = 16844074; // 0x101052a
+ field public static final int shortcutShortLabel = 16844073; // 0x1010529
field public static final int shouldDisableView = 16843246; // 0x10101ee
field public static final int showAsAction = 16843481; // 0x10102d9
field public static final int showDefault = 16843258; // 0x10101fa
field public static final int showDividers = 16843561; // 0x1010329
field public static final int showForAllUsers = 16844015; // 0x10104ef
+ field public static final int showMetadataInPreview = 16844079; // 0x101052f
field public static final deprecated int showOnLockScreen = 16843721; // 0x10103c9
field public static final int showSilent = 16843259; // 0x10101fb
field public static final int showText = 16843949; // 0x10104ad
@@ -3696,6 +3705,7 @@
method public void moveTaskToFront(int, int);
method public void moveTaskToFront(int, int, android.os.Bundle);
method public deprecated void restartPackage(java.lang.String);
+ method public static void setVrThread(int);
method public void setWatchHeapLimit(long);
field public static final java.lang.String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT";
field public static final int LOCK_TASK_MODE_LOCKED = 1; // 0x1
@@ -5037,12 +5047,14 @@
method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
method public java.lang.CharSequence getCancelLabel();
method public java.lang.CharSequence getConfirmLabel();
+ method public boolean getHintDisplayActionInline();
method public boolean getHintLaunchesActivity();
method public java.lang.CharSequence getInProgressLabel();
method public boolean isAvailableOffline();
method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
+ method public android.app.Notification.Action.WearableExtender setHintDisplayActionInline(boolean);
method public android.app.Notification.Action.WearableExtender setHintLaunchesActivity(boolean);
method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
}
@@ -5756,7 +5768,10 @@
method public android.content.pm.ServiceInfo getServiceInfo();
method public java.lang.String getServiceName();
method public java.lang.String getSettingsActivity();
+ method public boolean getShowMetadataInPreview();
method public java.lang.CharSequence loadAuthor(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException;
+ method public java.lang.CharSequence loadContextDescription(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException;
+ method public android.net.Uri loadContextUri(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException;
method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException;
method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
@@ -6484,11 +6499,13 @@
method public android.content.res.Configuration getConfiguration();
method public int getEventType();
method public java.lang.String getPackageName();
+ method public java.lang.String getShortcutId();
method public long getTimeStamp();
field public static final int CONFIGURATION_CHANGE = 5; // 0x5
field public static final int MOVE_TO_BACKGROUND = 2; // 0x2
field public static final int MOVE_TO_FOREGROUND = 1; // 0x1
field public static final int NONE = 0; // 0x0
+ field public static final int SHORTCUT_INVOCATION = 8; // 0x8
field public static final int USER_INTERACTION = 7; // 0x7
}
@@ -8191,6 +8208,7 @@
field public static final java.lang.String RESTRICTIONS_SERVICE = "restrictions";
field public static final java.lang.String SEARCH_SERVICE = "search";
field public static final java.lang.String SENSOR_SERVICE = "sensor";
+ field public static final java.lang.String SHORTCUT_SERVICE = "shortcut";
field public static final java.lang.String STORAGE_SERVICE = "storage";
field public static final java.lang.String SYSTEM_HEALTH_SERVICE = "systemhealth";
field public static final java.lang.String TELECOM_SERVICE = "telecom";
@@ -9500,13 +9518,20 @@
public class LauncherApps {
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
+ method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
+ method public android.graphics.drawable.Drawable getShortcutIconDrawable(android.content.pm.ShortcutInfo, int);
+ method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
+ method public boolean hasShortcutHostPermission();
method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
method public boolean isPackageEnabled(java.lang.String, android.os.UserHandle);
+ method public void pinShortcuts(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
method public void registerCallback(android.content.pm.LauncherApps.Callback);
method public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler);
method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+ method public void startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
+ method public void startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle);
method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
}
@@ -9519,6 +9544,20 @@
method public void onPackagesSuspended(java.lang.String[], android.os.UserHandle);
method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean);
method public void onPackagesUnsuspended(java.lang.String[], android.os.UserHandle);
+ method public void onShortcutsChanged(java.lang.String, java.util.List<android.content.pm.ShortcutInfo>, android.os.UserHandle);
+ }
+
+ public static class LauncherApps.ShortcutQuery {
+ ctor public LauncherApps.ShortcutQuery();
+ method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName);
+ method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long);
+ method public android.content.pm.LauncherApps.ShortcutQuery setPackage(java.lang.String);
+ method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int);
+ method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(java.util.List<java.lang.String>);
+ field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
+ field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1
+ field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8
+ field public static final int FLAG_MATCH_PINNED = 2; // 0x2
}
public class PackageInfo implements android.os.Parcelable {
@@ -10019,6 +10058,66 @@
field public java.lang.String permission;
}
+ public final class ShortcutInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.content.ComponentName getActivity();
+ method public java.util.Set<java.lang.String> getCategories();
+ method public java.lang.CharSequence getDisabledMessage();
+ method public android.os.PersistableBundle getExtras();
+ method public java.lang.String getId();
+ method public android.content.Intent getIntent();
+ method public android.content.Intent[] getIntents();
+ method public long getLastChangedTimestamp();
+ method public java.lang.CharSequence getLongLabel();
+ method public java.lang.String getPackage();
+ method public int getRank();
+ method public java.lang.CharSequence getShortLabel();
+ method public android.os.UserHandle getUserHandle();
+ method public boolean hasKeyFieldsOnly();
+ method public boolean isDeclaredInManifest();
+ method public boolean isDynamic();
+ method public boolean isEnabled();
+ method public boolean isImmutable();
+ method public boolean isPinned();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
+ field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
+ }
+
+ public static class ShortcutInfo.Builder {
+ ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String);
+ method public android.content.pm.ShortcutInfo build();
+ method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
+ method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>);
+ method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence);
+ method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
+ method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
+ method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
+ method public android.content.pm.ShortcutInfo.Builder setIntents(android.content.Intent[]);
+ method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.CharSequence);
+ method public android.content.pm.ShortcutInfo.Builder setRank(int);
+ method public android.content.pm.ShortcutInfo.Builder setShortLabel(java.lang.CharSequence);
+ }
+
+ public class ShortcutManager {
+ method public boolean addDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+ method public void disableShortcuts(java.util.List<java.lang.String>);
+ method public void disableShortcuts(java.util.List<java.lang.String>, java.lang.CharSequence);
+ method public void enableShortcuts(java.util.List<java.lang.String>);
+ method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
+ method public int getIconMaxHeight();
+ method public int getIconMaxWidth();
+ method public java.util.List<android.content.pm.ShortcutInfo> getManifestShortcuts();
+ method public int getMaxShortcutCountPerActivity();
+ method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
+ method public boolean isRateLimitingActive();
+ method public void removeAllDynamicShortcuts();
+ method public void removeDynamicShortcuts(java.util.List<java.lang.String>);
+ method public void reportShortcutUsed(java.lang.String);
+ method public boolean setDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+ method public boolean updateShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+ }
+
public class Signature implements android.os.Parcelable {
ctor public Signature(byte[]);
ctor public Signature(java.lang.String);
@@ -10225,7 +10324,7 @@
}
public class Resources {
- ctor public Resources(android.content.res.AssetManager, android.util.DisplayMetrics, android.content.res.Configuration);
+ ctor public deprecated Resources(android.content.res.AssetManager, android.util.DisplayMetrics, android.content.res.Configuration);
method public final void finishPreloading();
method public final void flushLayoutCache();
method public android.content.res.XmlResourceParser getAnimation(int) throws android.content.res.Resources.NotFoundException;
@@ -10276,7 +10375,7 @@
method public android.content.res.AssetFileDescriptor openRawResourceFd(int) throws android.content.res.Resources.NotFoundException;
method public void parseBundleExtra(java.lang.String, android.util.AttributeSet, android.os.Bundle) throws org.xmlpull.v1.XmlPullParserException;
method public void parseBundleExtras(android.content.res.XmlResourceParser, android.os.Bundle) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
- method public void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics);
+ method public deprecated void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics);
}
public static class Resources.NotFoundException extends java.lang.RuntimeException {
@@ -19556,6 +19655,7 @@
field public static final android.os.Parcelable.Creator<android.media.AudioFormat> CREATOR;
field public static final int ENCODING_AC3 = 5; // 0x5
field public static final int ENCODING_DEFAULT = 1; // 0x1
+ field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe
field public static final int ENCODING_DTS = 7; // 0x7
field public static final int ENCODING_DTS_HD = 8; // 0x8
field public static final int ENCODING_E_AC3 = 6; // 0x6
@@ -28268,6 +28368,7 @@
field public static final int LOLLIPOP_MR1 = 22; // 0x16
field public static final int M = 23; // 0x17
field public static final int N = 24; // 0x18
+ field public static final int N_MR1 = 25; // 0x19
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -29259,6 +29360,7 @@
method public android.os.Bundle getUserRestrictions();
method public android.os.Bundle getUserRestrictions(android.os.UserHandle);
method public boolean hasUserRestriction(java.lang.String);
+ method public boolean isDemoUser();
method public boolean isQuietModeEnabled(android.os.UserHandle);
method public boolean isSystemUser();
method public boolean isUserAGoat();
@@ -29498,6 +29600,7 @@
method public boolean isObbMounted(java.lang.String);
method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
+ field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
}
public final class StorageVolume implements android.os.Parcelable {
@@ -30640,6 +30743,7 @@
public static class CallLog.Calls implements android.provider.BaseColumns {
ctor public CallLog.Calls();
method public static java.lang.String getLastOutgoingCall(android.content.Context);
+ field public static final int ANSWERED_EXTERNALLY_TYPE = 7; // 0x7
field public static final int BLOCKED_TYPE = 6; // 0x6
field public static final java.lang.String CACHED_FORMATTED_NUMBER = "formatted_number";
field public static final java.lang.String CACHED_LOOKUP_URI = "lookup_uri";
@@ -30662,6 +30766,7 @@
field public static final java.lang.String DURATION = "duration";
field public static final java.lang.String EXTRA_CALL_TYPE_FILTER = "android.provider.extra.CALL_TYPE_FILTER";
field public static final java.lang.String FEATURES = "features";
+ field public static final int FEATURES_PULLED_EXTERNALLY = 2; // 0x2
field public static final int FEATURES_VIDEO = 1; // 0x1
field public static final java.lang.String GEOCODED_LOCATION = "geocoded_location";
field public static final int INCOMING_TYPE = 1; // 0x1
@@ -32367,6 +32472,7 @@
field public static final java.lang.String DATA_ROAMING = "data_roaming";
field public static final java.lang.String DEBUG_APP = "debug_app";
field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
+ field public static final java.lang.String DEVICE_NAME = "device_name";
field public static final java.lang.String DEVICE_PROVISIONED = "device_provisioned";
field public static final java.lang.String HTTP_PROXY = "http_proxy";
field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
@@ -32952,6 +33058,7 @@
field public static final java.lang.String ACTION_NEW_VOICEMAIL = "android.intent.action.NEW_VOICEMAIL";
field public static final java.lang.String ACTION_SYNC_VOICEMAIL = "android.provider.action.SYNC_VOICEMAIL";
field public static final java.lang.String AUTHORITY = "com.android.voicemail";
+ field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.provider.extra.PHONE_ACCOUNT_HANDLE";
field public static final java.lang.String EXTRA_SELF_CHANGE = "com.android.voicemail.extra.SELF_CHANGE";
field public static final java.lang.String PARAM_KEY_SOURCE_PACKAGE = "source_package";
}
@@ -32960,6 +33067,9 @@
method public static android.net.Uri buildSourceUri(java.lang.String);
field public static final java.lang.String CONFIGURATION_STATE = "configuration_state";
field public static final int CONFIGURATION_STATE_CAN_BE_CONFIGURED = 2; // 0x2
+ field public static final int CONFIGURATION_STATE_CONFIGURING = 3; // 0x3
+ field public static final int CONFIGURATION_STATE_DISABLED = 5; // 0x5
+ field public static final int CONFIGURATION_STATE_FAILED = 4; // 0x4
field public static final int CONFIGURATION_STATE_NOT_CONFIGURED = 1; // 0x1
field public static final int CONFIGURATION_STATE_OK = 0; // 0x0
field public static final android.net.Uri CONTENT_URI;
@@ -32984,6 +33094,7 @@
field public static final int QUOTA_UNAVAILABLE = -1; // 0xffffffff
field public static final java.lang.String SETTINGS_URI = "settings_uri";
field public static final java.lang.String SOURCE_PACKAGE = "source_package";
+ field public static final java.lang.String SOURCE_TYPE = "source_type";
field public static final java.lang.String VOICEMAIL_ACCESS_URI = "voicemail_access_uri";
}
@@ -35936,9 +36047,14 @@
method public void phoneAccountSelected(android.telecom.PhoneAccountHandle, boolean);
method public void playDtmfTone(char);
method public void postDialContinue(boolean);
+ method public void pullExternalCall();
+ method public final void putExtras(android.os.Bundle);
method public void registerCallback(android.telecom.Call.Callback);
method public void registerCallback(android.telecom.Call.Callback, android.os.Handler);
method public void reject(boolean, java.lang.String);
+ method public final void removeExtras(java.util.List<java.lang.String>);
+ method public final void removeExtras(java.lang.String...);
+ method public void sendCallEvent(java.lang.String, android.os.Bundle);
method public void splitFromConference();
method public void stopDtmfTone();
method public void swapConference();
@@ -35952,6 +36068,7 @@
field public static final int STATE_DISCONNECTING = 10; // 0xa
field public static final int STATE_HOLDING = 3; // 0x3
field public static final int STATE_NEW = 0; // 0x0
+ field public static final int STATE_PULLING_CALL = 11; // 0xb
field public static final int STATE_RINGING = 2; // 0x2
field public static final int STATE_SELECT_PHONE_ACCOUNT = 8; // 0x8
}
@@ -35962,6 +36079,7 @@
method public void onCannedTextResponsesLoaded(android.telecom.Call, java.util.List<java.lang.String>);
method public void onChildrenChanged(android.telecom.Call, java.util.List<android.telecom.Call>);
method public void onConferenceableCallsChanged(android.telecom.Call, java.util.List<android.telecom.Call>);
+ method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle);
method public void onDetailsChanged(android.telecom.Call, android.telecom.Call.Details);
method public void onParentChanged(android.telecom.Call, android.telecom.Call);
method public void onPostDialWait(android.telecom.Call, java.lang.String);
@@ -35992,6 +36110,7 @@
method public static java.lang.String propertiesToString(int);
field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 4194304; // 0x400000
field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
+ field public static final int CAPABILITY_CAN_PULL_CALL = 8388608; // 0x800000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
field public static final int CAPABILITY_HOLD = 1; // 0x1
field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
@@ -36011,7 +36130,9 @@
field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4
field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20
field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
+ field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
+ field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
field public static final int PROPERTY_WIFI = 8; // 0x8
}
@@ -36062,6 +36183,7 @@
method public final android.telecom.CallAudioState getCallAudioState();
method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final long getConnectionTime();
method public final java.util.List<android.telecom.Connection> getConnections();
method public final android.telecom.DisconnectCause getDisconnectCause();
@@ -36074,6 +36196,7 @@
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onConnectionAdded(android.telecom.Connection);
method public void onDisconnect();
+ method public void onExtrasChanged(android.os.Bundle);
method public void onHold();
method public void onMerge(android.telecom.Connection);
method public void onMerge();
@@ -36082,10 +36205,14 @@
method public void onStopDtmfTone();
method public void onSwap();
method public void onUnhold();
+ method public final void putExtras(android.os.Bundle);
method public final void removeConnection(android.telecom.Connection);
+ method public final void removeExtras(java.util.List<java.lang.String>);
+ method public final void removeExtras(java.lang.String...);
method public final void setActive();
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConnectionCapabilities(int);
+ method public final void setConnectionProperties(int);
method public final void setConnectionTime(long);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
@@ -36115,6 +36242,7 @@
method public final android.telecom.Conference getConference();
method public final java.util.List<android.telecom.Conferenceable> getConferenceables();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
method public final int getState();
@@ -36125,16 +36253,24 @@
method public void onAnswer(int);
method public void onAnswer();
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
+ method public void onCallEvent(java.lang.String, android.os.Bundle);
method public void onDisconnect();
+ method public void onExtrasChanged(android.os.Bundle);
method public void onHold();
method public void onPlayDtmfTone(char);
method public void onPostDialContinue(boolean);
+ method public void onPullExternalCall();
method public void onReject();
method public void onReject(java.lang.String);
method public void onSeparate();
method public void onStateChanged(int);
method public void onStopDtmfTone();
method public void onUnhold();
+ method public static java.lang.String propertiesToString(int);
+ method public final void putExtras(android.os.Bundle);
+ method public final void removeExtras(java.util.List<java.lang.String>);
+ method public final void removeExtras(java.lang.String...);
+ method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
method public final void setActive();
method public final void setAddress(android.net.Uri, int);
method public final void setAudioModeIsVoip(boolean);
@@ -36142,6 +36278,7 @@
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
method public final void setConnectionCapabilities(int);
+ method public final void setConnectionProperties(int);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
method public final void setExtras(android.os.Bundle);
@@ -36150,6 +36287,7 @@
method public final void setNextPostDialChar(char);
method public final void setOnHold();
method public final void setPostDialWait(java.lang.String);
+ method public final void setPulling();
method public final void setRingbackRequested(boolean);
method public final void setRinging();
method public final void setStatusHints(android.telecom.StatusHints);
@@ -36158,6 +36296,7 @@
method public static java.lang.String stateToString(int);
field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
+ field public static final int CAPABILITY_CAN_PULL_CALL = 16777216; // 0x1000000
field public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 4194304; // 0x400000
field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
@@ -36175,15 +36314,21 @@
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800
field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
+ field public static final java.lang.String EVENT_CALL_MERGE_FAILED = "android.telecom.event.CALL_MERGE_FAILED";
+ field public static final java.lang.String EVENT_CALL_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED";
+ field public static final java.lang.String EXTRA_ANSWERING_DROPS_FG_CALL = "android.telecom.extra.ANSWERING_DROPS_FG_CALL";
field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+ field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
+ field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_DIALING = 3; // 0x3
field public static final int STATE_DISCONNECTED = 6; // 0x6
field public static final int STATE_HOLDING = 5; // 0x5
field public static final int STATE_INITIALIZING = 0; // 0x0
field public static final int STATE_NEW = 1; // 0x1
+ field public static final int STATE_PULLING_CALL = 7; // 0x7
field public static final int STATE_RINGING = 2; // 0x2
}
@@ -36261,7 +36406,9 @@
method public java.lang.String getReason();
method public int getTone();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final int ANSWERED_ELSEWHERE = 11; // 0xb
field public static final int BUSY = 7; // 0x7
+ field public static final int CALL_PULLED = 12; // 0xc
field public static final int CANCELED = 4; // 0x4
field public static final int CONNECTION_MANAGER_NOT_SUPPORTED = 10; // 0xa
field public static final android.os.Parcelable.Creator<android.telecom.DisconnectCause> CREATOR;
@@ -36297,6 +36444,7 @@
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onCallRemoved(android.telecom.Call);
method public void onCanAddCallChanged(boolean);
+ method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle);
method public void onSilenceRinger();
method public final void setAudioRoute(int);
method public final void setMuted(boolean);
@@ -36419,6 +36567,7 @@
method public void onConferenceableConnectionsChanged(android.telecom.RemoteConference, java.util.List<android.telecom.RemoteConnection>);
method public void onConnectionAdded(android.telecom.RemoteConference, android.telecom.RemoteConnection);
method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConference, int);
+ method public void onConnectionPropertiesChanged(android.telecom.RemoteConference, int);
method public void onConnectionRemoved(android.telecom.RemoteConference, android.telecom.RemoteConnection);
method public void onDestroyed(android.telecom.RemoteConference);
method public void onDisconnected(android.telecom.RemoteConference, android.telecom.DisconnectCause);
@@ -36437,6 +36586,7 @@
method public android.telecom.RemoteConference getConference();
method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
method public int getConnectionCapabilities();
+ method public int getConnectionProperties();
method public android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
method public int getState();
@@ -36448,6 +36598,7 @@
method public boolean isVoipAudioMode();
method public void playDtmfTone(char);
method public void postDialContinue(boolean);
+ method public void pullExternalCall();
method public void registerCallback(android.telecom.RemoteConnection.Callback);
method public void registerCallback(android.telecom.RemoteConnection.Callback, android.os.Handler);
method public void reject();
@@ -36464,6 +36615,8 @@
method public void onConferenceChanged(android.telecom.RemoteConnection, android.telecom.RemoteConference);
method public void onConferenceableConnectionsChanged(android.telecom.RemoteConnection, java.util.List<android.telecom.RemoteConnection>);
method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConnection, int);
+ method public void onConnectionEvent(android.telecom.RemoteConnection, java.lang.String, android.os.Bundle);
+ method public void onConnectionPropertiesChanged(android.telecom.RemoteConnection, int);
method public void onDestroyed(android.telecom.RemoteConnection);
method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause);
method public void onExtrasChanged(android.telecom.RemoteConnection, android.os.Bundle);
@@ -36560,6 +36713,7 @@
field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE";
field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS";
field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE";
+ field public static final java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS";
field public static final java.lang.String METADATA_IN_CALL_SERVICE_RINGING = "android.telecom.IN_CALL_SERVICE_RINGING";
field public static final java.lang.String METADATA_IN_CALL_SERVICE_UI = "android.telecom.IN_CALL_SERVICE_UI";
field public static final int PRESENTATION_ALLOWED = 1; // 0x1
@@ -36614,9 +36768,11 @@
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
+ field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
field public static final java.lang.String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool";
field public static final java.lang.String KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL = "allow_emergency_video_calls_bool";
field public static final java.lang.String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
+ field public static final java.lang.String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = "allow_merge_wifi_calls_when_vowifi_off_bool";
field public static final java.lang.String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool";
field public static final java.lang.String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool";
field public static final java.lang.String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
@@ -36648,6 +36804,7 @@
field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
field public static final java.lang.String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
+ field public static final java.lang.String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL = "drop_video_call_when_answering_audio_call_bool";
field public static final java.lang.String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
@@ -36705,6 +36862,7 @@
field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
+ field public static final java.lang.String KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL = "treat_downgraded_video_calls_as_video_calls_bool";
field public static final java.lang.String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool";
field public static final java.lang.String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
field public static final java.lang.String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
@@ -37289,12 +37447,15 @@
field public static final int NETWORK_TYPE_EVDO_A = 6; // 0x6
field public static final int NETWORK_TYPE_EVDO_B = 12; // 0xc
field public static final int NETWORK_TYPE_GPRS = 1; // 0x1
+ field public static final int NETWORK_TYPE_GSM = 16; // 0x10
field public static final int NETWORK_TYPE_HSDPA = 8; // 0x8
field public static final int NETWORK_TYPE_HSPA = 10; // 0xa
field public static final int NETWORK_TYPE_HSPAP = 15; // 0xf
field public static final int NETWORK_TYPE_HSUPA = 9; // 0x9
field public static final int NETWORK_TYPE_IDEN = 11; // 0xb
+ field public static final int NETWORK_TYPE_IWLAN = 18; // 0x12
field public static final int NETWORK_TYPE_LTE = 13; // 0xd
+ field public static final int NETWORK_TYPE_TD_SCDMA = 17; // 0x11
field public static final int NETWORK_TYPE_UMTS = 3; // 0x3
field public static final int NETWORK_TYPE_UNKNOWN = 0; // 0x0
field public static final int PHONE_TYPE_CDMA = 2; // 0x2
@@ -40095,7 +40256,10 @@
method public boolean equals(android.util.DisplayMetrics);
method public void setTo(android.util.DisplayMetrics);
method public void setToDefaults();
+ field public static final int DENSITY_260 = 260; // 0x104
field public static final int DENSITY_280 = 280; // 0x118
+ field public static final int DENSITY_300 = 300; // 0x12c
+ field public static final int DENSITY_340 = 340; // 0x154
field public static final int DENSITY_360 = 360; // 0x168
field public static final int DENSITY_400 = 400; // 0x190
field public static final int DENSITY_420 = 420; // 0x1a4
@@ -41425,6 +41589,10 @@
field public static final int KEYCODE_SWITCH_CHARSET = 95; // 0x5f
field public static final int KEYCODE_SYM = 63; // 0x3f
field public static final int KEYCODE_SYSRQ = 120; // 0x78
+ field public static final int KEYCODE_SYSTEM_NAVIGATION_DOWN = 281; // 0x119
+ field public static final int KEYCODE_SYSTEM_NAVIGATION_LEFT = 282; // 0x11a
+ field public static final int KEYCODE_SYSTEM_NAVIGATION_RIGHT = 283; // 0x11b
+ field public static final int KEYCODE_SYSTEM_NAVIGATION_UP = 280; // 0x118
field public static final int KEYCODE_T = 48; // 0x30
field public static final int KEYCODE_TAB = 61; // 0x3d
field public static final int KEYCODE_TV = 170; // 0xaa
@@ -42307,6 +42475,7 @@
method public float getPivotY();
method public android.view.PointerIcon getPointerIcon();
method public android.content.res.Resources getResources();
+ method public final boolean getRevealOnFocusHint();
method public final int getRight();
method protected float getRightFadingEdgeStrength();
method protected int getRightPaddingOffset();
@@ -42594,6 +42763,7 @@
method public void setPivotY(float);
method public void setPointerIcon(android.view.PointerIcon);
method public void setPressed(boolean);
+ method public final void setRevealOnFocusHint(boolean);
method public final void setRight(int);
method public void setRotation(float);
method public void setRotationX(float);
@@ -43743,6 +43913,7 @@
field public static final int TYPE_APPLICATION_SUB_PANEL = 1002; // 0x3ea
field public static final int TYPE_BASE_APPLICATION = 1; // 0x1
field public static final int TYPE_CHANGED = 2; // 0x2
+ field public static final int TYPE_DRAWN_APPLICATION = 4; // 0x4
field public static final int TYPE_INPUT_METHOD = 2011; // 0x7db
field public static final int TYPE_INPUT_METHOD_DIALOG = 2012; // 0x7dc
field public static final int TYPE_KEYGUARD_DIALOG = 2009; // 0x7d9
@@ -44515,6 +44686,7 @@
method public boolean clearMetaKeyStates(int);
method public void closeConnection();
method public boolean commitCompletion(android.view.inputmethod.CompletionInfo);
+ method public boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
method public boolean deleteSurroundingText(int, int);
@@ -44624,6 +44796,7 @@
field public static final int IME_NULL = 0; // 0x0
field public int actionId;
field public java.lang.CharSequence actionLabel;
+ field public java.lang.String[] contentMimeTypes;
field public android.os.Bundle extras;
field public int fieldId;
field public java.lang.String fieldName;
@@ -44683,6 +44856,7 @@
method public abstract boolean clearMetaKeyStates(int);
method public abstract void closeConnection();
method public abstract boolean commitCompletion(android.view.inputmethod.CompletionInfo);
+ method public abstract boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle);
method public abstract boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public abstract boolean commitText(java.lang.CharSequence, int);
method public abstract boolean deleteSurroundingText(int, int);
@@ -44708,6 +44882,7 @@
field public static final int CURSOR_UPDATE_MONITOR = 2; // 0x2
field public static final int GET_EXTRACTED_TEXT_MONITOR = 1; // 0x1
field public static final int GET_TEXT_WITH_STYLES = 1; // 0x1
+ field public static final int INPUT_CONTENT_GRANT_READ_URI_PERMISSION = 1; // 0x1
}
public class InputConnectionWrapper implements android.view.inputmethod.InputConnection {
@@ -44716,6 +44891,7 @@
method public boolean clearMetaKeyStates(int);
method public void closeConnection();
method public boolean commitCompletion(android.view.inputmethod.CompletionInfo);
+ method public boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
method public boolean deleteSurroundingText(int, int);
@@ -44740,6 +44916,19 @@
method public void setTarget(android.view.inputmethod.InputConnection);
}
+ public final class InputContentInfo implements android.os.Parcelable {
+ ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription);
+ ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription, android.net.Uri);
+ method public int describeContents();
+ method public android.net.Uri getContentUri();
+ method public android.content.ClipDescription getDescription();
+ method public android.net.Uri getLinkUri();
+ method public void releasePermission();
+ method public void requestPermission();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.inputmethod.InputContentInfo> CREATOR;
+ }
+
public abstract interface InputMethod {
method public abstract void attachToken(android.os.IBinder);
method public abstract void bindInput(android.view.inputmethod.InputBinding);
diff --git a/api/system-current.txt b/api/system-current.txt
index d1c0394..c33fe6e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -504,6 +504,7 @@
field public static final int colorPressedHighlight = 16843661; // 0x101038d
field public static final int colorPrimary = 16843827; // 0x1010433
field public static final int colorPrimaryDark = 16843828; // 0x1010434
+ field public static final int colorSecondary = 16844080; // 0x1010530
field public static final int columnCount = 16843639; // 0x1010377
field public static final int columnDelay = 16843215; // 0x10101cf
field public static final int columnOrderPreserved = 16843640; // 0x1010378
@@ -527,7 +528,9 @@
field public static final int contentInsetStart = 16843859; // 0x1010453
field public static final int contentInsetStartWithNavigation = 16844066; // 0x1010522
field public static final int contextClickable = 16844007; // 0x10104e7
+ field public static final int contextDescription = 16844078; // 0x101052e
field public static final int contextPopupMenuStyle = 16844033; // 0x1010501
+ field public static final int contextUri = 16844077; // 0x101052d
field public static final int controlX1 = 16843772; // 0x10103fc
field public static final int controlX2 = 16843774; // 0x10103fe
field public static final int controlY1 = 16843773; // 0x10103fd
@@ -1146,6 +1149,7 @@
field public static final int rotation = 16843558; // 0x1010326
field public static final int rotationX = 16843559; // 0x1010327
field public static final int rotationY = 16843560; // 0x1010328
+ field public static final int roundIcon = 16844076; // 0x101052c
field public static final int rowCount = 16843637; // 0x1010375
field public static final int rowDelay = 16843216; // 0x10101d0
field public static final int rowEdgeFlags = 16843329; // 0x1010241
@@ -1217,11 +1221,16 @@
field public static final int shareInterpolator = 16843195; // 0x10101bb
field public static final int sharedUserId = 16842763; // 0x101000b
field public static final int sharedUserLabel = 16843361; // 0x1010261
+ field public static final int shortcutDisabledMessage = 16844075; // 0x101052b
+ field public static final int shortcutId = 16844072; // 0x1010528
+ field public static final int shortcutLongLabel = 16844074; // 0x101052a
+ field public static final int shortcutShortLabel = 16844073; // 0x1010529
field public static final int shouldDisableView = 16843246; // 0x10101ee
field public static final int showAsAction = 16843481; // 0x10102d9
field public static final int showDefault = 16843258; // 0x10101fa
field public static final int showDividers = 16843561; // 0x1010329
field public static final int showForAllUsers = 16844015; // 0x10104ef
+ field public static final int showMetadataInPreview = 16844079; // 0x101052f
field public static final deprecated int showOnLockScreen = 16843721; // 0x10103c9
field public static final int showSilent = 16843259; // 0x10101fb
field public static final int showText = 16843949; // 0x10104ad
@@ -3832,6 +3841,7 @@
method public void moveTaskToFront(int, int);
method public void moveTaskToFront(int, int, android.os.Bundle);
method public deprecated void restartPackage(java.lang.String);
+ method public static void setVrThread(int);
method public void setWatchHeapLimit(long);
field public static final java.lang.String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT";
field public static final int LOCK_TASK_MODE_LOCKED = 1; // 0x1
@@ -4515,6 +4525,15 @@
field public static final int VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION = 3; // 0x3
}
+ public abstract class EphemeralResolverService extends android.app.Service {
+ ctor public EphemeralResolverService();
+ method public final void attachBaseContext(android.content.Context);
+ method public final android.os.IBinder onBind(android.content.Intent);
+ method public abstract java.util.List<android.content.pm.EphemeralResolveInfo> onEphemeralResolveInfoList(int[], int);
+ field public static final java.lang.String EXTRA_RESOLVE_INFO = "android.app.extra.RESOLVE_INFO";
+ field public static final java.lang.String EXTRA_SEQUENCE = "android.app.extra.SEQUENCE";
+ }
+
public class ExpandableListActivity extends android.app.Activity implements android.widget.ExpandableListView.OnChildClickListener android.widget.ExpandableListView.OnGroupCollapseListener android.widget.ExpandableListView.OnGroupExpandListener android.view.View.OnCreateContextMenuListener {
ctor public ExpandableListActivity();
method public android.widget.ExpandableListAdapter getExpandableListAdapter();
@@ -5185,12 +5204,14 @@
method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
method public java.lang.CharSequence getCancelLabel();
method public java.lang.CharSequence getConfirmLabel();
+ method public boolean getHintDisplayActionInline();
method public boolean getHintLaunchesActivity();
method public java.lang.CharSequence getInProgressLabel();
method public boolean isAvailableOffline();
method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
+ method public android.app.Notification.Action.WearableExtender setHintDisplayActionInline(boolean);
method public android.app.Notification.Action.WearableExtender setHintLaunchesActivity(boolean);
method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
}
@@ -5904,7 +5925,10 @@
method public android.content.pm.ServiceInfo getServiceInfo();
method public java.lang.String getServiceName();
method public java.lang.String getSettingsActivity();
+ method public boolean getShowMetadataInPreview();
method public java.lang.CharSequence loadAuthor(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException;
+ method public java.lang.CharSequence loadContextDescription(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException;
+ method public android.net.Uri loadContextUri(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException;
method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException;
method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
@@ -6767,11 +6791,13 @@
method public android.content.res.Configuration getConfiguration();
method public int getEventType();
method public java.lang.String getPackageName();
+ method public java.lang.String getShortcutId();
method public long getTimeStamp();
field public static final int CONFIGURATION_CHANGE = 5; // 0x5
field public static final int MOVE_TO_BACKGROUND = 2; // 0x2
field public static final int MOVE_TO_FOREGROUND = 1; // 0x1
field public static final int NONE = 0; // 0x0
+ field public static final int SHORTCUT_INVOCATION = 8; // 0x8
field public static final int USER_INTERACTION = 7; // 0x7
}
@@ -8515,6 +8541,7 @@
field public static final java.lang.String RESTRICTIONS_SERVICE = "restrictions";
field public static final java.lang.String SEARCH_SERVICE = "search";
field public static final java.lang.String SENSOR_SERVICE = "sensor";
+ field public static final java.lang.String SHORTCUT_SERVICE = "shortcut";
field public static final java.lang.String STORAGE_SERVICE = "storage";
field public static final java.lang.String SYSTEM_HEALTH_SERVICE = "systemhealth";
field public static final java.lang.String TELECOM_SERVICE = "telecom";
@@ -9855,13 +9882,20 @@
public class LauncherApps {
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
+ method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
+ method public android.graphics.drawable.Drawable getShortcutIconDrawable(android.content.pm.ShortcutInfo, int);
+ method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
+ method public boolean hasShortcutHostPermission();
method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
method public boolean isPackageEnabled(java.lang.String, android.os.UserHandle);
+ method public void pinShortcuts(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
method public void registerCallback(android.content.pm.LauncherApps.Callback);
method public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler);
method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+ method public void startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
+ method public void startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle);
method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
}
@@ -9874,6 +9908,20 @@
method public void onPackagesSuspended(java.lang.String[], android.os.UserHandle);
method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean);
method public void onPackagesUnsuspended(java.lang.String[], android.os.UserHandle);
+ method public void onShortcutsChanged(java.lang.String, java.util.List<android.content.pm.ShortcutInfo>, android.os.UserHandle);
+ }
+
+ public static class LauncherApps.ShortcutQuery {
+ ctor public LauncherApps.ShortcutQuery();
+ method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName);
+ method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long);
+ method public android.content.pm.LauncherApps.ShortcutQuery setPackage(java.lang.String);
+ method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int);
+ method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(java.util.List<java.lang.String>);
+ field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
+ field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1
+ field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8
+ field public static final int FLAG_MATCH_PINNED = 2; // 0x2
}
public class PackageInfo implements android.os.Parcelable {
@@ -10444,6 +10492,66 @@
field public java.lang.String permission;
}
+ public final class ShortcutInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.content.ComponentName getActivity();
+ method public java.util.Set<java.lang.String> getCategories();
+ method public java.lang.CharSequence getDisabledMessage();
+ method public android.os.PersistableBundle getExtras();
+ method public java.lang.String getId();
+ method public android.content.Intent getIntent();
+ method public android.content.Intent[] getIntents();
+ method public long getLastChangedTimestamp();
+ method public java.lang.CharSequence getLongLabel();
+ method public java.lang.String getPackage();
+ method public int getRank();
+ method public java.lang.CharSequence getShortLabel();
+ method public android.os.UserHandle getUserHandle();
+ method public boolean hasKeyFieldsOnly();
+ method public boolean isDeclaredInManifest();
+ method public boolean isDynamic();
+ method public boolean isEnabled();
+ method public boolean isImmutable();
+ method public boolean isPinned();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
+ field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
+ }
+
+ public static class ShortcutInfo.Builder {
+ ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String);
+ method public android.content.pm.ShortcutInfo build();
+ method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
+ method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>);
+ method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence);
+ method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
+ method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
+ method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
+ method public android.content.pm.ShortcutInfo.Builder setIntents(android.content.Intent[]);
+ method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.CharSequence);
+ method public android.content.pm.ShortcutInfo.Builder setRank(int);
+ method public android.content.pm.ShortcutInfo.Builder setShortLabel(java.lang.CharSequence);
+ }
+
+ public class ShortcutManager {
+ method public boolean addDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+ method public void disableShortcuts(java.util.List<java.lang.String>);
+ method public void disableShortcuts(java.util.List<java.lang.String>, java.lang.CharSequence);
+ method public void enableShortcuts(java.util.List<java.lang.String>);
+ method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
+ method public int getIconMaxHeight();
+ method public int getIconMaxWidth();
+ method public java.util.List<android.content.pm.ShortcutInfo> getManifestShortcuts();
+ method public int getMaxShortcutCountPerActivity();
+ method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
+ method public boolean isRateLimitingActive();
+ method public void removeAllDynamicShortcuts();
+ method public void removeDynamicShortcuts(java.util.List<java.lang.String>);
+ method public void reportShortcutUsed(java.lang.String);
+ method public boolean setDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+ method public boolean updateShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+ }
+
public class Signature implements android.os.Parcelable {
ctor public Signature(byte[]);
ctor public Signature(java.lang.String);
@@ -10664,7 +10772,7 @@
}
public class Resources {
- ctor public Resources(android.content.res.AssetManager, android.util.DisplayMetrics, android.content.res.Configuration);
+ ctor public deprecated Resources(android.content.res.AssetManager, android.util.DisplayMetrics, android.content.res.Configuration);
method public final void finishPreloading();
method public final void flushLayoutCache();
method public android.content.res.XmlResourceParser getAnimation(int) throws android.content.res.Resources.NotFoundException;
@@ -10715,7 +10823,7 @@
method public android.content.res.AssetFileDescriptor openRawResourceFd(int) throws android.content.res.Resources.NotFoundException;
method public void parseBundleExtra(java.lang.String, android.util.AttributeSet, android.os.Bundle) throws org.xmlpull.v1.XmlPullParserException;
method public void parseBundleExtras(android.content.res.XmlResourceParser, android.os.Bundle) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
- method public void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics);
+ method public deprecated void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics);
}
public static class Resources.NotFoundException extends java.lang.RuntimeException {
@@ -21064,6 +21172,7 @@
field public static final android.os.Parcelable.Creator<android.media.AudioFormat> CREATOR;
field public static final int ENCODING_AC3 = 5; // 0x5
field public static final int ENCODING_DEFAULT = 1; // 0x1
+ field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe
field public static final int ENCODING_DTS = 7; // 0x7
field public static final int ENCODING_DTS_HD = 8; // 0x8
field public static final int ENCODING_E_AC3 = 6; // 0x6
@@ -25916,6 +26025,33 @@
package android.net.metrics {
+ public final class ApfProgramEvent implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.net.metrics.ApfProgramEvent> CREATOR;
+ field public static final int FLAG_HAS_IPV4_ADDRESS = 1; // 0x1
+ field public static final int FLAG_MULTICAST_FILTER_ON = 0; // 0x0
+ field public final int currentRas;
+ field public final int filteredRas;
+ field public final int flags;
+ field public final long lifetime;
+ field public final int programLength;
+ }
+
+ public final class ApfStats implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.net.metrics.ApfStats> CREATOR;
+ field public final int droppedRas;
+ field public final long durationMs;
+ field public final int matchingRas;
+ field public final int maxProgramSize;
+ field public final int parseErrors;
+ field public final int programUpdates;
+ field public final int receivedRas;
+ field public final int zeroLifetimeRas;
+ }
+
public final class DefaultNetworkEvent implements android.os.Parcelable {
method public int describeContents();
method public static void logEvent(int, int[], int, boolean, boolean);
@@ -25933,6 +26069,7 @@
method public static void logStateEvent(java.lang.String, java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.metrics.DhcpClientEvent> CREATOR;
+ field public final int durationMs;
field public final java.lang.String ifName;
field public final java.lang.String msg;
}
@@ -26024,6 +26161,18 @@
field public final int netId;
}
+ public final class RaEvent implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.net.metrics.RaEvent> CREATOR;
+ field public final long dnsslLifetime;
+ field public final long prefixPreferredLifetime;
+ field public final long prefixValidLifetime;
+ field public final long rdnssLifetime;
+ field public final long routeInfoLifetime;
+ field public final long routerLifetime;
+ }
+
public final class ValidationProbeEvent implements android.os.Parcelable {
method public int describeContents();
method public static void logEvent(int, long, int, int);
@@ -30709,6 +30858,7 @@
field public static final int LOLLIPOP_MR1 = 22; // 0x16
field public static final int M = 23; // 0x17
field public static final int N = 24; // 0x18
+ field public static final int N_MR1 = 25; // 0x19
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -31544,6 +31694,7 @@
method public static void installPackage(android.content.Context, java.io.File, boolean) throws java.io.IOException;
method public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener, android.os.Handler) throws java.io.IOException;
method public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener) throws java.io.IOException;
+ method public static void rebootWipeAb(android.content.Context, java.io.File, java.lang.String) throws java.io.IOException;
method public static void rebootWipeCache(android.content.Context) throws java.io.IOException;
method public static void rebootWipeUserData(android.content.Context) throws java.io.IOException;
method public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException;
@@ -31784,6 +31935,7 @@
method public android.os.Bundle getUserRestrictions();
method public android.os.Bundle getUserRestrictions(android.os.UserHandle);
method public boolean hasUserRestriction(java.lang.String);
+ method public boolean isDemoUser();
method public boolean isManagedProfile();
method public boolean isManagedProfile(int);
method public boolean isQuietModeEnabled(android.os.UserHandle);
@@ -32032,6 +32184,7 @@
method public boolean isObbMounted(java.lang.String);
method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
+ field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
}
public final class StorageVolume implements android.os.Parcelable {
@@ -33213,6 +33366,7 @@
public static class CallLog.Calls implements android.provider.BaseColumns {
ctor public CallLog.Calls();
method public static java.lang.String getLastOutgoingCall(android.content.Context);
+ field public static final int ANSWERED_EXTERNALLY_TYPE = 7; // 0x7
field public static final int BLOCKED_TYPE = 6; // 0x6
field public static final java.lang.String CACHED_FORMATTED_NUMBER = "formatted_number";
field public static final java.lang.String CACHED_LOOKUP_URI = "lookup_uri";
@@ -33235,6 +33389,7 @@
field public static final java.lang.String DURATION = "duration";
field public static final java.lang.String EXTRA_CALL_TYPE_FILTER = "android.provider.extra.CALL_TYPE_FILTER";
field public static final java.lang.String FEATURES = "features";
+ field public static final int FEATURES_PULLED_EXTERNALLY = 2; // 0x2
field public static final int FEATURES_VIDEO = 1; // 0x1
field public static final java.lang.String GEOCODED_LOCATION = "geocoded_location";
field public static final int INCOMING_TYPE = 1; // 0x1
@@ -35072,6 +35227,7 @@
field public static final java.lang.String DATA_ROAMING = "data_roaming";
field public static final java.lang.String DEBUG_APP = "debug_app";
field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
+ field public static final java.lang.String DEVICE_NAME = "device_name";
field public static final java.lang.String DEVICE_PROVISIONED = "device_provisioned";
field public static final java.lang.String HTTP_PROXY = "http_proxy";
field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
@@ -35660,6 +35816,7 @@
field public static final java.lang.String ACTION_NEW_VOICEMAIL = "android.intent.action.NEW_VOICEMAIL";
field public static final java.lang.String ACTION_SYNC_VOICEMAIL = "android.provider.action.SYNC_VOICEMAIL";
field public static final java.lang.String AUTHORITY = "com.android.voicemail";
+ field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.provider.extra.PHONE_ACCOUNT_HANDLE";
field public static final java.lang.String EXTRA_SELF_CHANGE = "com.android.voicemail.extra.SELF_CHANGE";
field public static final java.lang.String PARAM_KEY_SOURCE_PACKAGE = "source_package";
}
@@ -35668,6 +35825,9 @@
method public static android.net.Uri buildSourceUri(java.lang.String);
field public static final java.lang.String CONFIGURATION_STATE = "configuration_state";
field public static final int CONFIGURATION_STATE_CAN_BE_CONFIGURED = 2; // 0x2
+ field public static final int CONFIGURATION_STATE_CONFIGURING = 3; // 0x3
+ field public static final int CONFIGURATION_STATE_DISABLED = 5; // 0x5
+ field public static final int CONFIGURATION_STATE_FAILED = 4; // 0x4
field public static final int CONFIGURATION_STATE_NOT_CONFIGURED = 1; // 0x1
field public static final int CONFIGURATION_STATE_OK = 0; // 0x0
field public static final android.net.Uri CONTENT_URI;
@@ -35692,6 +35852,7 @@
field public static final int QUOTA_UNAVAILABLE = -1; // 0xffffffff
field public static final java.lang.String SETTINGS_URI = "settings_uri";
field public static final java.lang.String SOURCE_PACKAGE = "source_package";
+ field public static final java.lang.String SOURCE_TYPE = "source_type";
field public static final java.lang.String VOICEMAIL_ACCESS_URI = "voicemail_access_uri";
}
@@ -38772,10 +38933,15 @@
method public void phoneAccountSelected(android.telecom.PhoneAccountHandle, boolean);
method public void playDtmfTone(char);
method public void postDialContinue(boolean);
+ method public void pullExternalCall();
+ method public final void putExtras(android.os.Bundle);
method public void registerCallback(android.telecom.Call.Callback);
method public void registerCallback(android.telecom.Call.Callback, android.os.Handler);
method public void reject(boolean, java.lang.String);
+ method public final void removeExtras(java.util.List<java.lang.String>);
+ method public final void removeExtras(java.lang.String...);
method public deprecated void removeListener(android.telecom.Call.Listener);
+ method public void sendCallEvent(java.lang.String, android.os.Bundle);
method public void splitFromConference();
method public void stopDtmfTone();
method public void swapConference();
@@ -38790,6 +38956,7 @@
field public static final int STATE_HOLDING = 3; // 0x3
field public static final int STATE_NEW = 0; // 0x0
field public static final deprecated int STATE_PRE_DIAL_WAIT = 8; // 0x8
+ field public static final int STATE_PULLING_CALL = 11; // 0xb
field public static final int STATE_RINGING = 2; // 0x2
field public static final int STATE_SELECT_PHONE_ACCOUNT = 8; // 0x8
}
@@ -38800,6 +38967,7 @@
method public void onCannedTextResponsesLoaded(android.telecom.Call, java.util.List<java.lang.String>);
method public void onChildrenChanged(android.telecom.Call, java.util.List<android.telecom.Call>);
method public void onConferenceableCallsChanged(android.telecom.Call, java.util.List<android.telecom.Call>);
+ method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle);
method public void onDetailsChanged(android.telecom.Call, android.telecom.Call.Details);
method public void onParentChanged(android.telecom.Call, android.telecom.Call);
method public void onPostDialWait(android.telecom.Call, java.lang.String);
@@ -38830,6 +38998,7 @@
method public static java.lang.String propertiesToString(int);
field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 4194304; // 0x400000
field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
+ field public static final int CAPABILITY_CAN_PULL_CALL = 8388608; // 0x800000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
field public static final int CAPABILITY_HOLD = 1; // 0x1
field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
@@ -38849,7 +39018,9 @@
field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4
field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20
field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
+ field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
+ field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
field public static final int PROPERTY_WIFI = 8; // 0x8
}
@@ -38906,6 +39077,7 @@
method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
method public final deprecated long getConnectTimeMillis();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final long getConnectionTime();
method public final java.util.List<android.telecom.Connection> getConnections();
method public final android.telecom.DisconnectCause getDisconnectCause();
@@ -38920,6 +39092,7 @@
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onConnectionAdded(android.telecom.Connection);
method public void onDisconnect();
+ method public void onExtrasChanged(android.os.Bundle);
method public void onHold();
method public void onMerge(android.telecom.Connection);
method public void onMerge();
@@ -38928,11 +39101,15 @@
method public void onStopDtmfTone();
method public void onSwap();
method public void onUnhold();
+ method public final void putExtras(android.os.Bundle);
method public final void removeConnection(android.telecom.Connection);
+ method public final void removeExtras(java.util.List<java.lang.String>);
+ method public final void removeExtras(java.lang.String...);
method public final void setActive();
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final deprecated void setConnectTimeMillis(long);
method public final void setConnectionCapabilities(int);
+ method public final void setConnectionProperties(int);
method public final void setConnectionTime(long);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
@@ -38963,6 +39140,7 @@
method public final android.telecom.Conference getConference();
method public final java.util.List<android.telecom.Conferenceable> getConferenceables();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
method public final int getState();
@@ -38974,16 +39152,24 @@
method public void onAnswer();
method public deprecated void onAudioStateChanged(android.telecom.AudioState);
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
+ method public void onCallEvent(java.lang.String, android.os.Bundle);
method public void onDisconnect();
+ method public void onExtrasChanged(android.os.Bundle);
method public void onHold();
method public void onPlayDtmfTone(char);
method public void onPostDialContinue(boolean);
+ method public void onPullExternalCall();
method public void onReject();
method public void onReject(java.lang.String);
method public void onSeparate();
method public void onStateChanged(int);
method public void onStopDtmfTone();
method public void onUnhold();
+ method public static java.lang.String propertiesToString(int);
+ method public final void putExtras(android.os.Bundle);
+ method public final void removeExtras(java.util.List<java.lang.String>);
+ method public final void removeExtras(java.lang.String...);
+ method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
method public final void setActive();
method public final void setAddress(android.net.Uri, int);
method public final void setAudioModeIsVoip(boolean);
@@ -38991,6 +39177,7 @@
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
method public final void setConnectionCapabilities(int);
+ method public final void setConnectionProperties(int);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
method public final void setExtras(android.os.Bundle);
@@ -38999,6 +39186,7 @@
method public final void setNextPostDialChar(char);
method public final void setOnHold();
method public final void setPostDialWait(java.lang.String);
+ method public final void setPulling();
method public final void setRingbackRequested(boolean);
method public final void setRinging();
method public final void setStatusHints(android.telecom.StatusHints);
@@ -39007,6 +39195,7 @@
method public static java.lang.String stateToString(int);
field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
+ field public static final int CAPABILITY_CAN_PULL_CALL = 16777216; // 0x1000000
field public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 4194304; // 0x400000
field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
@@ -39024,15 +39213,21 @@
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800
field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
+ field public static final java.lang.String EVENT_CALL_MERGE_FAILED = "android.telecom.event.CALL_MERGE_FAILED";
+ field public static final java.lang.String EVENT_CALL_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED";
+ field public static final java.lang.String EXTRA_ANSWERING_DROPS_FG_CALL = "android.telecom.extra.ANSWERING_DROPS_FG_CALL";
field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+ field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
+ field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_DIALING = 3; // 0x3
field public static final int STATE_DISCONNECTED = 6; // 0x6
field public static final int STATE_HOLDING = 5; // 0x5
field public static final int STATE_INITIALIZING = 0; // 0x0
field public static final int STATE_NEW = 1; // 0x1
+ field public static final int STATE_PULLING_CALL = 7; // 0x7
field public static final int STATE_RINGING = 2; // 0x2
}
@@ -39110,7 +39305,9 @@
method public java.lang.String getReason();
method public int getTone();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final int ANSWERED_ELSEWHERE = 11; // 0xb
field public static final int BUSY = 7; // 0x7
+ field public static final int CALL_PULLED = 12; // 0xc
field public static final int CANCELED = 4; // 0x4
field public static final int CONNECTION_MANAGER_NOT_SUPPORTED = 10; // 0xa
field public static final android.os.Parcelable.Creator<android.telecom.DisconnectCause> CREATOR;
@@ -39147,6 +39344,7 @@
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onCallRemoved(android.telecom.Call);
method public void onCanAddCallChanged(boolean);
+ method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle);
method public deprecated void onPhoneCreated(android.telecom.Phone);
method public deprecated void onPhoneDestroyed(android.telecom.Phone);
method public void onSilenceRinger();
@@ -39184,14 +39382,16 @@
}
public class ParcelableCallAnalytics implements android.os.Parcelable {
- ctor public ParcelableCallAnalytics(long, long, int, boolean, boolean, int, int, boolean, java.lang.String, boolean);
+ ctor public ParcelableCallAnalytics(long, long, int, boolean, boolean, int, int, boolean, java.lang.String, boolean, java.util.List<android.telecom.ParcelableCallAnalytics.AnalyticsEvent>, java.util.List<android.telecom.ParcelableCallAnalytics.EventTiming>);
ctor public ParcelableCallAnalytics(android.os.Parcel);
+ method public java.util.List<android.telecom.ParcelableCallAnalytics.AnalyticsEvent> analyticsEvents();
method public int describeContents();
method public long getCallDurationMillis();
method public int getCallTechnologies();
method public int getCallTerminationCode();
method public int getCallType();
method public java.lang.String getConnectionService();
+ method public java.util.List<android.telecom.ParcelableCallAnalytics.EventTiming> getEventTimings();
method public long getStartTimeMillis();
method public boolean isAdditionalCall();
method public boolean isCreatedFromExistingConnection();
@@ -39212,6 +39412,73 @@
field public static final int THIRD_PARTY_PHONE = 16; // 0x10
}
+ public static final class ParcelableCallAnalytics.AnalyticsEvent implements android.os.Parcelable {
+ ctor public ParcelableCallAnalytics.AnalyticsEvent(int, long);
+ method public int describeContents();
+ method public int getEventName();
+ method public long getTimeSinceLastEvent();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int AUDIO_ROUTE_BT = 204; // 0xcc
+ field public static final int AUDIO_ROUTE_EARPIECE = 205; // 0xcd
+ field public static final int AUDIO_ROUTE_HEADSET = 206; // 0xce
+ field public static final int AUDIO_ROUTE_SPEAKER = 207; // 0xcf
+ field public static final int BIND_CS = 5; // 0x5
+ field public static final int BLOCK_CHECK_FINISHED = 105; // 0x69
+ field public static final int BLOCK_CHECK_INITIATED = 104; // 0x68
+ field public static final int CONFERENCE_WITH = 300; // 0x12c
+ field public static final android.os.Parcelable.Creator<android.telecom.ParcelableCallAnalytics.AnalyticsEvent> CREATOR;
+ field public static final int CS_BOUND = 6; // 0x6
+ field public static final int DIRECT_TO_VM_FINISHED = 103; // 0x67
+ field public static final int DIRECT_TO_VM_INITIATED = 102; // 0x66
+ field public static final int FILTERING_COMPLETED = 107; // 0x6b
+ field public static final int FILTERING_INITIATED = 106; // 0x6a
+ field public static final int FILTERING_TIMED_OUT = 108; // 0x6c
+ field public static final int MUTE = 202; // 0xca
+ field public static final int REMOTELY_HELD = 402; // 0x192
+ field public static final int REMOTELY_UNHELD = 403; // 0x193
+ field public static final int REQUEST_ACCEPT = 7; // 0x7
+ field public static final int REQUEST_HOLD = 400; // 0x190
+ field public static final int REQUEST_PULL = 500; // 0x1f4
+ field public static final int REQUEST_REJECT = 8; // 0x8
+ field public static final int REQUEST_UNHOLD = 401; // 0x191
+ field public static final int SCREENING_COMPLETED = 101; // 0x65
+ field public static final int SCREENING_SENT = 100; // 0x64
+ field public static final int SET_ACTIVE = 1; // 0x1
+ field public static final int SET_DIALING = 4; // 0x4
+ field public static final int SET_DISCONNECTED = 2; // 0x2
+ field public static final int SET_HOLD = 404; // 0x194
+ field public static final int SET_PARENT = 302; // 0x12e
+ field public static final int SET_SELECT_PHONE_ACCOUNT = 0; // 0x0
+ field public static final int SILENCE = 201; // 0xc9
+ field public static final int SKIP_RINGING = 200; // 0xc8
+ field public static final int SPLIT_CONFERENCE = 301; // 0x12d
+ field public static final int START_CONNECTION = 3; // 0x3
+ field public static final int SWAP = 405; // 0x195
+ field public static final int UNMUTE = 203; // 0xcb
+ }
+
+ public static final class ParcelableCallAnalytics.EventTiming implements android.os.Parcelable {
+ ctor public ParcelableCallAnalytics.EventTiming(int, long);
+ method public int describeContents();
+ method public int getName();
+ method public long getTime();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int ACCEPT_TIMING = 0; // 0x0
+ field public static final int BIND_CS_TIMING = 6; // 0x6
+ field public static final int BLOCK_CHECK_FINISHED_TIMING = 9; // 0x9
+ field public static final android.os.Parcelable.Creator<android.telecom.ParcelableCallAnalytics.EventTiming> CREATOR;
+ field public static final int DIRECT_TO_VM_FINISHED_TIMING = 8; // 0x8
+ field public static final int DISCONNECT_TIMING = 2; // 0x2
+ field public static final int FILTERING_COMPLETED_TIMING = 10; // 0xa
+ field public static final int FILTERING_TIMED_OUT_TIMING = 11; // 0xb
+ field public static final int HOLD_TIMING = 3; // 0x3
+ field public static final int INVALID = 999999; // 0xf423f
+ field public static final int OUTGOING_TIME_TO_DIALING_TIMING = 5; // 0x5
+ field public static final int REJECT_TIMING = 1; // 0x1
+ field public static final int SCREENING_COMPLETED_TIMING = 7; // 0x7
+ field public static final int UNHOLD_TIMING = 4; // 0x4
+ }
+
public final deprecated class Phone {
method public final void addListener(android.telecom.Phone.Listener);
method public final boolean canAddCall();
@@ -39324,6 +39591,7 @@
method public void onConferenceableConnectionsChanged(android.telecom.RemoteConference, java.util.List<android.telecom.RemoteConnection>);
method public void onConnectionAdded(android.telecom.RemoteConference, android.telecom.RemoteConnection);
method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConference, int);
+ method public void onConnectionPropertiesChanged(android.telecom.RemoteConference, int);
method public void onConnectionRemoved(android.telecom.RemoteConference, android.telecom.RemoteConnection);
method public void onDestroyed(android.telecom.RemoteConference);
method public void onDisconnected(android.telecom.RemoteConference, android.telecom.DisconnectCause);
@@ -39342,6 +39610,7 @@
method public android.telecom.RemoteConference getConference();
method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
method public int getConnectionCapabilities();
+ method public int getConnectionProperties();
method public android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
method public int getState();
@@ -39353,6 +39622,7 @@
method public boolean isVoipAudioMode();
method public void playDtmfTone(char);
method public void postDialContinue(boolean);
+ method public void pullExternalCall();
method public void registerCallback(android.telecom.RemoteConnection.Callback);
method public void registerCallback(android.telecom.RemoteConnection.Callback, android.os.Handler);
method public void reject();
@@ -39370,6 +39640,8 @@
method public void onConferenceChanged(android.telecom.RemoteConnection, android.telecom.RemoteConference);
method public void onConferenceableConnectionsChanged(android.telecom.RemoteConnection, java.util.List<android.telecom.RemoteConnection>);
method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConnection, int);
+ method public void onConnectionEvent(android.telecom.RemoteConnection, java.lang.String, android.os.Bundle);
+ method public void onConnectionPropertiesChanged(android.telecom.RemoteConnection, int);
method public void onDestroyed(android.telecom.RemoteConnection);
method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause);
method public void onExtrasChanged(android.telecom.RemoteConnection, android.os.Bundle);
@@ -39423,6 +39695,41 @@
field public static final android.os.Parcelable.Creator<android.telecom.StatusHints> CREATOR;
}
+ public final class TelecomAnalytics implements android.os.Parcelable {
+ ctor public TelecomAnalytics(java.util.List<android.telecom.TelecomAnalytics.SessionTiming>, java.util.List<android.telecom.ParcelableCallAnalytics>);
+ method public int describeContents();
+ method public java.util.List<android.telecom.ParcelableCallAnalytics> getCallAnalytics();
+ method public java.util.List<android.telecom.TelecomAnalytics.SessionTiming> getSessionTimings();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telecom.TelecomAnalytics> CREATOR;
+ }
+
+ public static final class TelecomAnalytics.SessionTiming implements android.os.Parcelable {
+ ctor public TelecomAnalytics.SessionTiming(int, long);
+ method public int describeContents();
+ method public java.lang.Integer getKey();
+ method public long getTime();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telecom.TelecomAnalytics.SessionTiming> CREATOR;
+ field public static final int CSW_ADD_CONFERENCE_CALL = 108; // 0x6c
+ field public static final int CSW_HANDLE_CREATE_CONNECTION_COMPLETE = 100; // 0x64
+ field public static final int CSW_REMOVE_CALL = 106; // 0x6a
+ field public static final int CSW_SET_ACTIVE = 101; // 0x65
+ field public static final int CSW_SET_DIALING = 103; // 0x67
+ field public static final int CSW_SET_DISCONNECTED = 104; // 0x68
+ field public static final int CSW_SET_IS_CONFERENCED = 107; // 0x6b
+ field public static final int CSW_SET_ON_HOLD = 105; // 0x69
+ field public static final int CSW_SET_RINGING = 102; // 0x66
+ field public static final int ICA_ANSWER_CALL = 1; // 0x1
+ field public static final int ICA_CONFERENCE = 8; // 0x8
+ field public static final int ICA_DISCONNECT_CALL = 3; // 0x3
+ field public static final int ICA_HOLD_CALL = 4; // 0x4
+ field public static final int ICA_MUTE = 6; // 0x6
+ field public static final int ICA_REJECT_CALL = 2; // 0x2
+ field public static final int ICA_SET_AUDIO_ROUTE = 7; // 0x7
+ field public static final int ICA_UNHOLD_CALL = 5; // 0x5
+ }
+
public class TelecomManager {
method public void acceptRingingCall();
method public void acceptRingingCall(int);
@@ -39432,7 +39739,7 @@
method public deprecated void clearAccounts();
method public void clearPhoneAccounts();
method public android.content.Intent createManageBlockedNumbersIntent();
- method public java.util.List<android.telecom.ParcelableCallAnalytics> dumpAnalytics();
+ method public android.telecom.TelecomAnalytics dumpAnalytics();
method public void enablePhoneAccount(android.telecom.PhoneAccountHandle, boolean);
method public boolean endCall();
method public android.net.Uri getAdnUriForPhoneAccount(android.telecom.PhoneAccountHandle);
@@ -39493,6 +39800,7 @@
field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE";
field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS";
field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE";
+ field public static final java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS";
field public static final java.lang.String METADATA_IN_CALL_SERVICE_RINGING = "android.telecom.IN_CALL_SERVICE_RINGING";
field public static final java.lang.String METADATA_IN_CALL_SERVICE_UI = "android.telecom.IN_CALL_SERVICE_UI";
field public static final int PRESENTATION_ALLOWED = 1; // 0x1
@@ -39549,9 +39857,11 @@
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
+ field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
field public static final java.lang.String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool";
field public static final java.lang.String KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL = "allow_emergency_video_calls_bool";
field public static final java.lang.String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
+ field public static final java.lang.String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = "allow_merge_wifi_calls_when_vowifi_off_bool";
field public static final java.lang.String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool";
field public static final java.lang.String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool";
field public static final java.lang.String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
@@ -39583,6 +39893,7 @@
field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
field public static final java.lang.String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
+ field public static final java.lang.String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL = "drop_video_call_when_answering_audio_call_bool";
field public static final java.lang.String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
@@ -39640,6 +39951,7 @@
field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
+ field public static final java.lang.String KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL = "treat_downgraded_video_calls_as_video_calls_bool";
field public static final java.lang.String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool";
field public static final java.lang.String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
field public static final java.lang.String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
@@ -40135,6 +40447,26 @@
method public void onSubscriptionsChanged();
}
+ public final class TelephonyHistogram implements android.os.Parcelable {
+ ctor public TelephonyHistogram(int, int, int);
+ ctor public TelephonyHistogram(android.telephony.TelephonyHistogram);
+ ctor public TelephonyHistogram(android.os.Parcel);
+ method public void addTimeTaken(int);
+ method public int describeContents();
+ method public int getAverageTime();
+ method public int getBucketCount();
+ method public int[] getBucketCounters();
+ method public int[] getBucketEndPoints();
+ method public int getCategory();
+ method public int getId();
+ method public int getMaxTime();
+ method public int getMinTime();
+ method public int getSampleCount();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.TelephonyHistogram> CREATOR;
+ field public static final int TELEPHONY_CATEGORY_RIL = 1; // 0x1
+ }
+
public class TelephonyManager {
method public void answerRingingCall();
method public void call(java.lang.String, java.lang.String);
@@ -40184,6 +40516,7 @@
method public java.lang.String getSimSerialNumber();
method public int getSimState();
method public java.lang.String getSubscriberId();
+ method public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
method public java.lang.String getVoiceMailAlphaTag();
method public java.lang.String getVoiceMailNumber();
method public int getVoiceNetworkType();
@@ -40207,6 +40540,7 @@
method public boolean isSmsCapable();
method public boolean isTtyModeSupported();
method public boolean isVideoCallingEnabled();
+ method public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
method public boolean isVoiceCapable();
method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
method public boolean isWorldPhone();
@@ -40220,6 +40554,7 @@
method public boolean setPreferredNetworkTypeToGlobal();
method public boolean setRadio(boolean);
method public boolean setRadioPower(boolean);
+ method public void setVisualVoicemailEnabled(android.telecom.PhoneAccountHandle, boolean);
method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
method public void silenceRinger();
method public boolean supplyPin(java.lang.String);
@@ -40272,12 +40607,15 @@
field public static final int NETWORK_TYPE_EVDO_A = 6; // 0x6
field public static final int NETWORK_TYPE_EVDO_B = 12; // 0xc
field public static final int NETWORK_TYPE_GPRS = 1; // 0x1
+ field public static final int NETWORK_TYPE_GSM = 16; // 0x10
field public static final int NETWORK_TYPE_HSDPA = 8; // 0x8
field public static final int NETWORK_TYPE_HSPA = 10; // 0xa
field public static final int NETWORK_TYPE_HSPAP = 15; // 0xf
field public static final int NETWORK_TYPE_HSUPA = 9; // 0x9
field public static final int NETWORK_TYPE_IDEN = 11; // 0xb
+ field public static final int NETWORK_TYPE_IWLAN = 18; // 0x12
field public static final int NETWORK_TYPE_LTE = 13; // 0xd
+ field public static final int NETWORK_TYPE_TD_SCDMA = 17; // 0x11
field public static final int NETWORK_TYPE_UMTS = 3; // 0x3
field public static final int NETWORK_TYPE_UNKNOWN = 0; // 0x0
field public static final int PHONE_TYPE_CDMA = 2; // 0x2
@@ -43095,7 +43433,10 @@
method public boolean equals(android.util.DisplayMetrics);
method public void setTo(android.util.DisplayMetrics);
method public void setToDefaults();
+ field public static final int DENSITY_260 = 260; // 0x104
field public static final int DENSITY_280 = 280; // 0x118
+ field public static final int DENSITY_300 = 300; // 0x12c
+ field public static final int DENSITY_340 = 340; // 0x154
field public static final int DENSITY_360 = 360; // 0x168
field public static final int DENSITY_400 = 400; // 0x190
field public static final int DENSITY_420 = 420; // 0x1a4
@@ -44425,6 +44766,10 @@
field public static final int KEYCODE_SWITCH_CHARSET = 95; // 0x5f
field public static final int KEYCODE_SYM = 63; // 0x3f
field public static final int KEYCODE_SYSRQ = 120; // 0x78
+ field public static final int KEYCODE_SYSTEM_NAVIGATION_DOWN = 281; // 0x119
+ field public static final int KEYCODE_SYSTEM_NAVIGATION_LEFT = 282; // 0x11a
+ field public static final int KEYCODE_SYSTEM_NAVIGATION_RIGHT = 283; // 0x11b
+ field public static final int KEYCODE_SYSTEM_NAVIGATION_UP = 280; // 0x118
field public static final int KEYCODE_T = 48; // 0x30
field public static final int KEYCODE_TAB = 61; // 0x3d
field public static final int KEYCODE_TV = 170; // 0xaa
@@ -45307,6 +45652,7 @@
method public float getPivotY();
method public android.view.PointerIcon getPointerIcon();
method public android.content.res.Resources getResources();
+ method public final boolean getRevealOnFocusHint();
method public final int getRight();
method protected float getRightFadingEdgeStrength();
method protected int getRightPaddingOffset();
@@ -45594,6 +45940,7 @@
method public void setPivotY(float);
method public void setPointerIcon(android.view.PointerIcon);
method public void setPressed(boolean);
+ method public final void setRevealOnFocusHint(boolean);
method public final void setRight(int);
method public void setRotation(float);
method public void setRotationX(float);
@@ -46746,6 +47093,7 @@
field public static final int TYPE_APPLICATION_SUB_PANEL = 1002; // 0x3ea
field public static final int TYPE_BASE_APPLICATION = 1; // 0x1
field public static final int TYPE_CHANGED = 2; // 0x2
+ field public static final int TYPE_DRAWN_APPLICATION = 4; // 0x4
field public static final int TYPE_INPUT_METHOD = 2011; // 0x7db
field public static final int TYPE_INPUT_METHOD_DIALOG = 2012; // 0x7dc
field public static final int TYPE_KEYGUARD_DIALOG = 2009; // 0x7d9
@@ -47518,6 +47866,7 @@
method public boolean clearMetaKeyStates(int);
method public void closeConnection();
method public boolean commitCompletion(android.view.inputmethod.CompletionInfo);
+ method public boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
method public boolean deleteSurroundingText(int, int);
@@ -47627,6 +47976,7 @@
field public static final int IME_NULL = 0; // 0x0
field public int actionId;
field public java.lang.CharSequence actionLabel;
+ field public java.lang.String[] contentMimeTypes;
field public android.os.Bundle extras;
field public int fieldId;
field public java.lang.String fieldName;
@@ -47686,6 +48036,7 @@
method public abstract boolean clearMetaKeyStates(int);
method public abstract void closeConnection();
method public abstract boolean commitCompletion(android.view.inputmethod.CompletionInfo);
+ method public abstract boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle);
method public abstract boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public abstract boolean commitText(java.lang.CharSequence, int);
method public abstract boolean deleteSurroundingText(int, int);
@@ -47711,6 +48062,7 @@
field public static final int CURSOR_UPDATE_MONITOR = 2; // 0x2
field public static final int GET_EXTRACTED_TEXT_MONITOR = 1; // 0x1
field public static final int GET_TEXT_WITH_STYLES = 1; // 0x1
+ field public static final int INPUT_CONTENT_GRANT_READ_URI_PERMISSION = 1; // 0x1
}
public class InputConnectionWrapper implements android.view.inputmethod.InputConnection {
@@ -47719,6 +48071,7 @@
method public boolean clearMetaKeyStates(int);
method public void closeConnection();
method public boolean commitCompletion(android.view.inputmethod.CompletionInfo);
+ method public boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
method public boolean deleteSurroundingText(int, int);
@@ -47743,6 +48096,19 @@
method public void setTarget(android.view.inputmethod.InputConnection);
}
+ public final class InputContentInfo implements android.os.Parcelable {
+ ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription);
+ ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription, android.net.Uri);
+ method public int describeContents();
+ method public android.net.Uri getContentUri();
+ method public android.content.ClipDescription getDescription();
+ method public android.net.Uri getLinkUri();
+ method public void releasePermission();
+ method public void requestPermission();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.inputmethod.InputContentInfo> CREATOR;
+ }
+
public abstract interface InputMethod {
method public abstract void attachToken(android.os.IBinder);
method public abstract void bindInput(android.view.inputmethod.InputBinding);
diff --git a/api/test-current.txt b/api/test-current.txt
index a70e9e3..3b5c223 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -397,6 +397,7 @@
field public static final int colorPressedHighlight = 16843661; // 0x101038d
field public static final int colorPrimary = 16843827; // 0x1010433
field public static final int colorPrimaryDark = 16843828; // 0x1010434
+ field public static final int colorSecondary = 16844080; // 0x1010530
field public static final int columnCount = 16843639; // 0x1010377
field public static final int columnDelay = 16843215; // 0x10101cf
field public static final int columnOrderPreserved = 16843640; // 0x1010378
@@ -420,7 +421,9 @@
field public static final int contentInsetStart = 16843859; // 0x1010453
field public static final int contentInsetStartWithNavigation = 16844066; // 0x1010522
field public static final int contextClickable = 16844007; // 0x10104e7
+ field public static final int contextDescription = 16844078; // 0x101052e
field public static final int contextPopupMenuStyle = 16844033; // 0x1010501
+ field public static final int contextUri = 16844077; // 0x101052d
field public static final int controlX1 = 16843772; // 0x10103fc
field public static final int controlX2 = 16843774; // 0x10103fe
field public static final int controlY1 = 16843773; // 0x10103fd
@@ -1039,6 +1042,7 @@
field public static final int rotation = 16843558; // 0x1010326
field public static final int rotationX = 16843559; // 0x1010327
field public static final int rotationY = 16843560; // 0x1010328
+ field public static final int roundIcon = 16844076; // 0x101052c
field public static final int rowCount = 16843637; // 0x1010375
field public static final int rowDelay = 16843216; // 0x10101d0
field public static final int rowEdgeFlags = 16843329; // 0x1010241
@@ -1106,11 +1110,16 @@
field public static final int shareInterpolator = 16843195; // 0x10101bb
field public static final int sharedUserId = 16842763; // 0x101000b
field public static final int sharedUserLabel = 16843361; // 0x1010261
+ field public static final int shortcutDisabledMessage = 16844075; // 0x101052b
+ field public static final int shortcutId = 16844072; // 0x1010528
+ field public static final int shortcutLongLabel = 16844074; // 0x101052a
+ field public static final int shortcutShortLabel = 16844073; // 0x1010529
field public static final int shouldDisableView = 16843246; // 0x10101ee
field public static final int showAsAction = 16843481; // 0x10102d9
field public static final int showDefault = 16843258; // 0x10101fa
field public static final int showDividers = 16843561; // 0x1010329
field public static final int showForAllUsers = 16844015; // 0x10104ef
+ field public static final int showMetadataInPreview = 16844079; // 0x101052f
field public static final deprecated int showOnLockScreen = 16843721; // 0x10103c9
field public static final int showSilent = 16843259; // 0x10101fb
field public static final int showText = 16843949; // 0x10104ad
@@ -3696,6 +3705,7 @@
method public void moveTaskToFront(int, int);
method public void moveTaskToFront(int, int, android.os.Bundle);
method public deprecated void restartPackage(java.lang.String);
+ method public static void setVrThread(int);
method public void setWatchHeapLimit(long);
field public static final java.lang.String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT";
field public static final int LOCK_TASK_MODE_LOCKED = 1; // 0x1
@@ -5038,12 +5048,14 @@
method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
method public java.lang.CharSequence getCancelLabel();
method public java.lang.CharSequence getConfirmLabel();
+ method public boolean getHintDisplayActionInline();
method public boolean getHintLaunchesActivity();
method public java.lang.CharSequence getInProgressLabel();
method public boolean isAvailableOffline();
method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
+ method public android.app.Notification.Action.WearableExtender setHintDisplayActionInline(boolean);
method public android.app.Notification.Action.WearableExtender setHintLaunchesActivity(boolean);
method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
}
@@ -5762,7 +5774,10 @@
method public android.content.pm.ServiceInfo getServiceInfo();
method public java.lang.String getServiceName();
method public java.lang.String getSettingsActivity();
+ method public boolean getShowMetadataInPreview();
method public java.lang.CharSequence loadAuthor(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException;
+ method public java.lang.CharSequence loadContextDescription(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException;
+ method public android.net.Uri loadContextUri(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException;
method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException;
method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
@@ -6490,11 +6505,13 @@
method public android.content.res.Configuration getConfiguration();
method public int getEventType();
method public java.lang.String getPackageName();
+ method public java.lang.String getShortcutId();
method public long getTimeStamp();
field public static final int CONFIGURATION_CHANGE = 5; // 0x5
field public static final int MOVE_TO_BACKGROUND = 2; // 0x2
field public static final int MOVE_TO_FOREGROUND = 1; // 0x1
field public static final int NONE = 0; // 0x0
+ field public static final int SHORTCUT_INVOCATION = 8; // 0x8
field public static final int USER_INTERACTION = 7; // 0x7
}
@@ -8199,6 +8216,7 @@
field public static final java.lang.String RESTRICTIONS_SERVICE = "restrictions";
field public static final java.lang.String SEARCH_SERVICE = "search";
field public static final java.lang.String SENSOR_SERVICE = "sensor";
+ field public static final java.lang.String SHORTCUT_SERVICE = "shortcut";
field public static final java.lang.String STORAGE_SERVICE = "storage";
field public static final java.lang.String SYSTEM_HEALTH_SERVICE = "systemhealth";
field public static final java.lang.String TELECOM_SERVICE = "telecom";
@@ -9512,13 +9530,20 @@
public class LauncherApps {
ctor public LauncherApps(android.content.Context);
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
+ method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
+ method public android.graphics.drawable.Drawable getShortcutIconDrawable(android.content.pm.ShortcutInfo, int);
+ method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
+ method public boolean hasShortcutHostPermission();
method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
method public boolean isPackageEnabled(java.lang.String, android.os.UserHandle);
+ method public void pinShortcuts(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
method public void registerCallback(android.content.pm.LauncherApps.Callback);
method public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler);
method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+ method public void startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
+ method public void startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle);
method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
}
@@ -9531,6 +9556,20 @@
method public void onPackagesSuspended(java.lang.String[], android.os.UserHandle);
method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean);
method public void onPackagesUnsuspended(java.lang.String[], android.os.UserHandle);
+ method public void onShortcutsChanged(java.lang.String, java.util.List<android.content.pm.ShortcutInfo>, android.os.UserHandle);
+ }
+
+ public static class LauncherApps.ShortcutQuery {
+ ctor public LauncherApps.ShortcutQuery();
+ method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName);
+ method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long);
+ method public android.content.pm.LauncherApps.ShortcutQuery setPackage(java.lang.String);
+ method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int);
+ method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(java.util.List<java.lang.String>);
+ field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
+ field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1
+ field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8
+ field public static final int FLAG_MATCH_PINNED = 2; // 0x2
}
public class PackageInfo implements android.os.Parcelable {
@@ -10032,6 +10071,67 @@
field public java.lang.String permission;
}
+ public final class ShortcutInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.content.ComponentName getActivity();
+ method public java.util.Set<java.lang.String> getCategories();
+ method public java.lang.CharSequence getDisabledMessage();
+ method public android.os.PersistableBundle getExtras();
+ method public java.lang.String getId();
+ method public android.content.Intent getIntent();
+ method public android.content.Intent[] getIntents();
+ method public long getLastChangedTimestamp();
+ method public java.lang.CharSequence getLongLabel();
+ method public java.lang.String getPackage();
+ method public int getRank();
+ method public java.lang.CharSequence getShortLabel();
+ method public android.os.UserHandle getUserHandle();
+ method public boolean hasKeyFieldsOnly();
+ method public boolean isDeclaredInManifest();
+ method public boolean isDynamic();
+ method public boolean isEnabled();
+ method public boolean isImmutable();
+ method public boolean isPinned();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
+ field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
+ }
+
+ public static class ShortcutInfo.Builder {
+ ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String);
+ method public android.content.pm.ShortcutInfo build();
+ method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
+ method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>);
+ method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence);
+ method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
+ method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
+ method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
+ method public android.content.pm.ShortcutInfo.Builder setIntents(android.content.Intent[]);
+ method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.CharSequence);
+ method public android.content.pm.ShortcutInfo.Builder setRank(int);
+ method public android.content.pm.ShortcutInfo.Builder setShortLabel(java.lang.CharSequence);
+ }
+
+ public class ShortcutManager {
+ ctor public ShortcutManager(android.content.Context);
+ method public boolean addDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+ method public void disableShortcuts(java.util.List<java.lang.String>);
+ method public void disableShortcuts(java.util.List<java.lang.String>, java.lang.CharSequence);
+ method public void enableShortcuts(java.util.List<java.lang.String>);
+ method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
+ method public int getIconMaxHeight();
+ method public int getIconMaxWidth();
+ method public java.util.List<android.content.pm.ShortcutInfo> getManifestShortcuts();
+ method public int getMaxShortcutCountPerActivity();
+ method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
+ method public boolean isRateLimitingActive();
+ method public void removeAllDynamicShortcuts();
+ method public void removeDynamicShortcuts(java.util.List<java.lang.String>);
+ method public void reportShortcutUsed(java.lang.String);
+ method public boolean setDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+ method public boolean updateShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+ }
+
public class Signature implements android.os.Parcelable {
ctor public Signature(byte[]);
ctor public Signature(java.lang.String);
@@ -10238,7 +10338,7 @@
}
public class Resources {
- ctor public Resources(android.content.res.AssetManager, android.util.DisplayMetrics, android.content.res.Configuration);
+ ctor public deprecated Resources(android.content.res.AssetManager, android.util.DisplayMetrics, android.content.res.Configuration);
method public final void finishPreloading();
method public final void flushLayoutCache();
method public android.content.res.XmlResourceParser getAnimation(int) throws android.content.res.Resources.NotFoundException;
@@ -10289,7 +10389,7 @@
method public android.content.res.AssetFileDescriptor openRawResourceFd(int) throws android.content.res.Resources.NotFoundException;
method public void parseBundleExtra(java.lang.String, android.util.AttributeSet, android.os.Bundle) throws org.xmlpull.v1.XmlPullParserException;
method public void parseBundleExtras(android.content.res.XmlResourceParser, android.os.Bundle) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
- method public void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics);
+ method public deprecated void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics);
}
public static class Resources.NotFoundException extends java.lang.RuntimeException {
@@ -19625,6 +19725,7 @@
field public static final android.os.Parcelable.Creator<android.media.AudioFormat> CREATOR;
field public static final int ENCODING_AC3 = 5; // 0x5
field public static final int ENCODING_DEFAULT = 1; // 0x1
+ field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe
field public static final int ENCODING_DTS = 7; // 0x7
field public static final int ENCODING_DTS_HD = 8; // 0x8
field public static final int ENCODING_E_AC3 = 6; // 0x6
@@ -28337,6 +28438,7 @@
field public static final int LOLLIPOP_MR1 = 22; // 0x16
field public static final int M = 23; // 0x17
field public static final int N = 24; // 0x18
+ field public static final int N_MR1 = 25; // 0x19
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -29126,6 +29228,7 @@
method public static final long getStartElapsedRealtime();
method public static final long getStartUptimeMillis();
method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException;
+ method public static final int getThreadScheduler(int) throws java.lang.IllegalArgumentException;
method public static final int getUidForName(java.lang.String);
method public static final boolean is64Bit();
method public static boolean isApplicationUid(int);
@@ -29329,6 +29432,7 @@
method public android.os.Bundle getUserRestrictions();
method public android.os.Bundle getUserRestrictions(android.os.UserHandle);
method public boolean hasUserRestriction(java.lang.String);
+ method public boolean isDemoUser();
method public boolean isQuietModeEnabled(android.os.UserHandle);
method public boolean isSystemUser();
method public boolean isUserAGoat();
@@ -29568,6 +29672,7 @@
method public boolean isObbMounted(java.lang.String);
method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
+ field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
}
public final class StorageVolume implements android.os.Parcelable {
@@ -30713,6 +30818,7 @@
public static class CallLog.Calls implements android.provider.BaseColumns {
ctor public CallLog.Calls();
method public static java.lang.String getLastOutgoingCall(android.content.Context);
+ field public static final int ANSWERED_EXTERNALLY_TYPE = 7; // 0x7
field public static final int BLOCKED_TYPE = 6; // 0x6
field public static final java.lang.String CACHED_FORMATTED_NUMBER = "formatted_number";
field public static final java.lang.String CACHED_LOOKUP_URI = "lookup_uri";
@@ -30735,6 +30841,7 @@
field public static final java.lang.String DURATION = "duration";
field public static final java.lang.String EXTRA_CALL_TYPE_FILTER = "android.provider.extra.CALL_TYPE_FILTER";
field public static final java.lang.String FEATURES = "features";
+ field public static final int FEATURES_PULLED_EXTERNALLY = 2; // 0x2
field public static final int FEATURES_VIDEO = 1; // 0x1
field public static final java.lang.String GEOCODED_LOCATION = "geocoded_location";
field public static final int INCOMING_TYPE = 1; // 0x1
@@ -32440,6 +32547,7 @@
field public static final java.lang.String DATA_ROAMING = "data_roaming";
field public static final java.lang.String DEBUG_APP = "debug_app";
field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
+ field public static final java.lang.String DEVICE_NAME = "device_name";
field public static final java.lang.String DEVICE_PROVISIONED = "device_provisioned";
field public static final java.lang.String HTTP_PROXY = "http_proxy";
field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
@@ -33028,6 +33136,7 @@
field public static final java.lang.String ACTION_NEW_VOICEMAIL = "android.intent.action.NEW_VOICEMAIL";
field public static final java.lang.String ACTION_SYNC_VOICEMAIL = "android.provider.action.SYNC_VOICEMAIL";
field public static final java.lang.String AUTHORITY = "com.android.voicemail";
+ field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.provider.extra.PHONE_ACCOUNT_HANDLE";
field public static final java.lang.String EXTRA_SELF_CHANGE = "com.android.voicemail.extra.SELF_CHANGE";
field public static final java.lang.String PARAM_KEY_SOURCE_PACKAGE = "source_package";
}
@@ -33036,6 +33145,9 @@
method public static android.net.Uri buildSourceUri(java.lang.String);
field public static final java.lang.String CONFIGURATION_STATE = "configuration_state";
field public static final int CONFIGURATION_STATE_CAN_BE_CONFIGURED = 2; // 0x2
+ field public static final int CONFIGURATION_STATE_CONFIGURING = 3; // 0x3
+ field public static final int CONFIGURATION_STATE_DISABLED = 5; // 0x5
+ field public static final int CONFIGURATION_STATE_FAILED = 4; // 0x4
field public static final int CONFIGURATION_STATE_NOT_CONFIGURED = 1; // 0x1
field public static final int CONFIGURATION_STATE_OK = 0; // 0x0
field public static final android.net.Uri CONTENT_URI;
@@ -33060,6 +33172,7 @@
field public static final int QUOTA_UNAVAILABLE = -1; // 0xffffffff
field public static final java.lang.String SETTINGS_URI = "settings_uri";
field public static final java.lang.String SOURCE_PACKAGE = "source_package";
+ field public static final java.lang.String SOURCE_TYPE = "source_type";
field public static final java.lang.String VOICEMAIL_ACCESS_URI = "voicemail_access_uri";
}
@@ -36013,9 +36126,14 @@
method public void phoneAccountSelected(android.telecom.PhoneAccountHandle, boolean);
method public void playDtmfTone(char);
method public void postDialContinue(boolean);
+ method public void pullExternalCall();
+ method public final void putExtras(android.os.Bundle);
method public void registerCallback(android.telecom.Call.Callback);
method public void registerCallback(android.telecom.Call.Callback, android.os.Handler);
method public void reject(boolean, java.lang.String);
+ method public final void removeExtras(java.util.List<java.lang.String>);
+ method public final void removeExtras(java.lang.String...);
+ method public void sendCallEvent(java.lang.String, android.os.Bundle);
method public void splitFromConference();
method public void stopDtmfTone();
method public void swapConference();
@@ -36029,6 +36147,7 @@
field public static final int STATE_DISCONNECTING = 10; // 0xa
field public static final int STATE_HOLDING = 3; // 0x3
field public static final int STATE_NEW = 0; // 0x0
+ field public static final int STATE_PULLING_CALL = 11; // 0xb
field public static final int STATE_RINGING = 2; // 0x2
field public static final int STATE_SELECT_PHONE_ACCOUNT = 8; // 0x8
}
@@ -36039,6 +36158,7 @@
method public void onCannedTextResponsesLoaded(android.telecom.Call, java.util.List<java.lang.String>);
method public void onChildrenChanged(android.telecom.Call, java.util.List<android.telecom.Call>);
method public void onConferenceableCallsChanged(android.telecom.Call, java.util.List<android.telecom.Call>);
+ method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle);
method public void onDetailsChanged(android.telecom.Call, android.telecom.Call.Details);
method public void onParentChanged(android.telecom.Call, android.telecom.Call);
method public void onPostDialWait(android.telecom.Call, java.lang.String);
@@ -36069,6 +36189,7 @@
method public static java.lang.String propertiesToString(int);
field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 4194304; // 0x400000
field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
+ field public static final int CAPABILITY_CAN_PULL_CALL = 8388608; // 0x800000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
field public static final int CAPABILITY_HOLD = 1; // 0x1
field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
@@ -36088,7 +36209,9 @@
field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4
field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20
field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
+ field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
+ field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
field public static final int PROPERTY_WIFI = 8; // 0x8
}
@@ -36139,6 +36262,7 @@
method public final android.telecom.CallAudioState getCallAudioState();
method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final long getConnectionTime();
method public final java.util.List<android.telecom.Connection> getConnections();
method public final android.telecom.DisconnectCause getDisconnectCause();
@@ -36151,6 +36275,7 @@
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onConnectionAdded(android.telecom.Connection);
method public void onDisconnect();
+ method public void onExtrasChanged(android.os.Bundle);
method public void onHold();
method public void onMerge(android.telecom.Connection);
method public void onMerge();
@@ -36159,10 +36284,14 @@
method public void onStopDtmfTone();
method public void onSwap();
method public void onUnhold();
+ method public final void putExtras(android.os.Bundle);
method public final void removeConnection(android.telecom.Connection);
+ method public final void removeExtras(java.util.List<java.lang.String>);
+ method public final void removeExtras(java.lang.String...);
method public final void setActive();
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConnectionCapabilities(int);
+ method public final void setConnectionProperties(int);
method public final void setConnectionTime(long);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
@@ -36192,6 +36321,7 @@
method public final android.telecom.Conference getConference();
method public final java.util.List<android.telecom.Conferenceable> getConferenceables();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
method public final int getState();
@@ -36202,16 +36332,24 @@
method public void onAnswer(int);
method public void onAnswer();
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
+ method public void onCallEvent(java.lang.String, android.os.Bundle);
method public void onDisconnect();
+ method public void onExtrasChanged(android.os.Bundle);
method public void onHold();
method public void onPlayDtmfTone(char);
method public void onPostDialContinue(boolean);
+ method public void onPullExternalCall();
method public void onReject();
method public void onReject(java.lang.String);
method public void onSeparate();
method public void onStateChanged(int);
method public void onStopDtmfTone();
method public void onUnhold();
+ method public static java.lang.String propertiesToString(int);
+ method public final void putExtras(android.os.Bundle);
+ method public final void removeExtras(java.util.List<java.lang.String>);
+ method public final void removeExtras(java.lang.String...);
+ method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
method public final void setActive();
method public final void setAddress(android.net.Uri, int);
method public final void setAudioModeIsVoip(boolean);
@@ -36219,6 +36357,7 @@
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
method public final void setConnectionCapabilities(int);
+ method public final void setConnectionProperties(int);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
method public final void setExtras(android.os.Bundle);
@@ -36227,6 +36366,7 @@
method public final void setNextPostDialChar(char);
method public final void setOnHold();
method public final void setPostDialWait(java.lang.String);
+ method public final void setPulling();
method public final void setRingbackRequested(boolean);
method public final void setRinging();
method public final void setStatusHints(android.telecom.StatusHints);
@@ -36235,6 +36375,7 @@
method public static java.lang.String stateToString(int);
field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
+ field public static final int CAPABILITY_CAN_PULL_CALL = 16777216; // 0x1000000
field public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 4194304; // 0x400000
field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
@@ -36252,15 +36393,21 @@
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800
field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
+ field public static final java.lang.String EVENT_CALL_MERGE_FAILED = "android.telecom.event.CALL_MERGE_FAILED";
+ field public static final java.lang.String EVENT_CALL_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED";
+ field public static final java.lang.String EXTRA_ANSWERING_DROPS_FG_CALL = "android.telecom.extra.ANSWERING_DROPS_FG_CALL";
field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+ field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
+ field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_DIALING = 3; // 0x3
field public static final int STATE_DISCONNECTED = 6; // 0x6
field public static final int STATE_HOLDING = 5; // 0x5
field public static final int STATE_INITIALIZING = 0; // 0x0
field public static final int STATE_NEW = 1; // 0x1
+ field public static final int STATE_PULLING_CALL = 7; // 0x7
field public static final int STATE_RINGING = 2; // 0x2
}
@@ -36338,7 +36485,9 @@
method public java.lang.String getReason();
method public int getTone();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final int ANSWERED_ELSEWHERE = 11; // 0xb
field public static final int BUSY = 7; // 0x7
+ field public static final int CALL_PULLED = 12; // 0xc
field public static final int CANCELED = 4; // 0x4
field public static final int CONNECTION_MANAGER_NOT_SUPPORTED = 10; // 0xa
field public static final android.os.Parcelable.Creator<android.telecom.DisconnectCause> CREATOR;
@@ -36374,6 +36523,7 @@
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onCallRemoved(android.telecom.Call);
method public void onCanAddCallChanged(boolean);
+ method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle);
method public void onSilenceRinger();
method public final void setAudioRoute(int);
method public final void setMuted(boolean);
@@ -36496,6 +36646,7 @@
method public void onConferenceableConnectionsChanged(android.telecom.RemoteConference, java.util.List<android.telecom.RemoteConnection>);
method public void onConnectionAdded(android.telecom.RemoteConference, android.telecom.RemoteConnection);
method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConference, int);
+ method public void onConnectionPropertiesChanged(android.telecom.RemoteConference, int);
method public void onConnectionRemoved(android.telecom.RemoteConference, android.telecom.RemoteConnection);
method public void onDestroyed(android.telecom.RemoteConference);
method public void onDisconnected(android.telecom.RemoteConference, android.telecom.DisconnectCause);
@@ -36514,6 +36665,7 @@
method public android.telecom.RemoteConference getConference();
method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
method public int getConnectionCapabilities();
+ method public int getConnectionProperties();
method public android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
method public int getState();
@@ -36525,6 +36677,7 @@
method public boolean isVoipAudioMode();
method public void playDtmfTone(char);
method public void postDialContinue(boolean);
+ method public void pullExternalCall();
method public void registerCallback(android.telecom.RemoteConnection.Callback);
method public void registerCallback(android.telecom.RemoteConnection.Callback, android.os.Handler);
method public void reject();
@@ -36541,6 +36694,8 @@
method public void onConferenceChanged(android.telecom.RemoteConnection, android.telecom.RemoteConference);
method public void onConferenceableConnectionsChanged(android.telecom.RemoteConnection, java.util.List<android.telecom.RemoteConnection>);
method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConnection, int);
+ method public void onConnectionEvent(android.telecom.RemoteConnection, java.lang.String, android.os.Bundle);
+ method public void onConnectionPropertiesChanged(android.telecom.RemoteConnection, int);
method public void onDestroyed(android.telecom.RemoteConnection);
method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause);
method public void onExtrasChanged(android.telecom.RemoteConnection, android.os.Bundle);
@@ -36637,6 +36792,7 @@
field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE";
field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS";
field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE";
+ field public static final java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS";
field public static final java.lang.String METADATA_IN_CALL_SERVICE_RINGING = "android.telecom.IN_CALL_SERVICE_RINGING";
field public static final java.lang.String METADATA_IN_CALL_SERVICE_UI = "android.telecom.IN_CALL_SERVICE_UI";
field public static final int PRESENTATION_ALLOWED = 1; // 0x1
@@ -36691,9 +36847,11 @@
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
+ field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
field public static final java.lang.String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool";
field public static final java.lang.String KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL = "allow_emergency_video_calls_bool";
field public static final java.lang.String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
+ field public static final java.lang.String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = "allow_merge_wifi_calls_when_vowifi_off_bool";
field public static final java.lang.String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool";
field public static final java.lang.String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool";
field public static final java.lang.String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
@@ -36725,6 +36883,7 @@
field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
field public static final java.lang.String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
+ field public static final java.lang.String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL = "drop_video_call_when_answering_audio_call_bool";
field public static final java.lang.String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
@@ -36782,6 +36941,7 @@
field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
+ field public static final java.lang.String KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL = "treat_downgraded_video_calls_as_video_calls_bool";
field public static final java.lang.String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool";
field public static final java.lang.String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
field public static final java.lang.String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
@@ -37366,12 +37526,15 @@
field public static final int NETWORK_TYPE_EVDO_A = 6; // 0x6
field public static final int NETWORK_TYPE_EVDO_B = 12; // 0xc
field public static final int NETWORK_TYPE_GPRS = 1; // 0x1
+ field public static final int NETWORK_TYPE_GSM = 16; // 0x10
field public static final int NETWORK_TYPE_HSDPA = 8; // 0x8
field public static final int NETWORK_TYPE_HSPA = 10; // 0xa
field public static final int NETWORK_TYPE_HSPAP = 15; // 0xf
field public static final int NETWORK_TYPE_HSUPA = 9; // 0x9
field public static final int NETWORK_TYPE_IDEN = 11; // 0xb
+ field public static final int NETWORK_TYPE_IWLAN = 18; // 0x12
field public static final int NETWORK_TYPE_LTE = 13; // 0xd
+ field public static final int NETWORK_TYPE_TD_SCDMA = 17; // 0x11
field public static final int NETWORK_TYPE_UMTS = 3; // 0x3
field public static final int NETWORK_TYPE_UNKNOWN = 0; // 0x0
field public static final int PHONE_TYPE_CDMA = 2; // 0x2
@@ -40174,7 +40337,10 @@
method public boolean equals(android.util.DisplayMetrics);
method public void setTo(android.util.DisplayMetrics);
method public void setToDefaults();
+ field public static final int DENSITY_260 = 260; // 0x104
field public static final int DENSITY_280 = 280; // 0x118
+ field public static final int DENSITY_300 = 300; // 0x12c
+ field public static final int DENSITY_340 = 340; // 0x154
field public static final int DENSITY_360 = 360; // 0x168
field public static final int DENSITY_400 = 400; // 0x190
field public static final int DENSITY_420 = 420; // 0x1a4
@@ -41504,6 +41670,10 @@
field public static final int KEYCODE_SWITCH_CHARSET = 95; // 0x5f
field public static final int KEYCODE_SYM = 63; // 0x3f
field public static final int KEYCODE_SYSRQ = 120; // 0x78
+ field public static final int KEYCODE_SYSTEM_NAVIGATION_DOWN = 281; // 0x119
+ field public static final int KEYCODE_SYSTEM_NAVIGATION_LEFT = 282; // 0x11a
+ field public static final int KEYCODE_SYSTEM_NAVIGATION_RIGHT = 283; // 0x11b
+ field public static final int KEYCODE_SYSTEM_NAVIGATION_UP = 280; // 0x118
field public static final int KEYCODE_T = 48; // 0x30
field public static final int KEYCODE_TAB = 61; // 0x3d
field public static final int KEYCODE_TV = 170; // 0xaa
@@ -42386,6 +42556,7 @@
method public float getPivotY();
method public android.view.PointerIcon getPointerIcon();
method public android.content.res.Resources getResources();
+ method public final boolean getRevealOnFocusHint();
method public final int getRight();
method protected float getRightFadingEdgeStrength();
method protected int getRightPaddingOffset();
@@ -42673,6 +42844,7 @@
method public void setPivotY(float);
method public void setPointerIcon(android.view.PointerIcon);
method public void setPressed(boolean);
+ method public final void setRevealOnFocusHint(boolean);
method public final void setRight(int);
method public void setRotation(float);
method public void setRotationX(float);
@@ -43822,6 +43994,7 @@
field public static final int TYPE_APPLICATION_SUB_PANEL = 1002; // 0x3ea
field public static final int TYPE_BASE_APPLICATION = 1; // 0x1
field public static final int TYPE_CHANGED = 2; // 0x2
+ field public static final int TYPE_DRAWN_APPLICATION = 4; // 0x4
field public static final int TYPE_INPUT_METHOD = 2011; // 0x7db
field public static final int TYPE_INPUT_METHOD_DIALOG = 2012; // 0x7dc
field public static final int TYPE_KEYGUARD_DIALOG = 2009; // 0x7d9
@@ -44594,6 +44767,7 @@
method public boolean clearMetaKeyStates(int);
method public void closeConnection();
method public boolean commitCompletion(android.view.inputmethod.CompletionInfo);
+ method public boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
method public boolean deleteSurroundingText(int, int);
@@ -44703,6 +44877,7 @@
field public static final int IME_NULL = 0; // 0x0
field public int actionId;
field public java.lang.CharSequence actionLabel;
+ field public java.lang.String[] contentMimeTypes;
field public android.os.Bundle extras;
field public int fieldId;
field public java.lang.String fieldName;
@@ -44762,6 +44937,7 @@
method public abstract boolean clearMetaKeyStates(int);
method public abstract void closeConnection();
method public abstract boolean commitCompletion(android.view.inputmethod.CompletionInfo);
+ method public abstract boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle);
method public abstract boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public abstract boolean commitText(java.lang.CharSequence, int);
method public abstract boolean deleteSurroundingText(int, int);
@@ -44787,6 +44963,7 @@
field public static final int CURSOR_UPDATE_MONITOR = 2; // 0x2
field public static final int GET_EXTRACTED_TEXT_MONITOR = 1; // 0x1
field public static final int GET_TEXT_WITH_STYLES = 1; // 0x1
+ field public static final int INPUT_CONTENT_GRANT_READ_URI_PERMISSION = 1; // 0x1
}
public class InputConnectionWrapper implements android.view.inputmethod.InputConnection {
@@ -44795,6 +44972,7 @@
method public boolean clearMetaKeyStates(int);
method public void closeConnection();
method public boolean commitCompletion(android.view.inputmethod.CompletionInfo);
+ method public boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
method public boolean deleteSurroundingText(int, int);
@@ -44819,6 +44997,19 @@
method public void setTarget(android.view.inputmethod.InputConnection);
}
+ public final class InputContentInfo implements android.os.Parcelable {
+ ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription);
+ ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription, android.net.Uri);
+ method public int describeContents();
+ method public android.net.Uri getContentUri();
+ method public android.content.ClipDescription getDescription();
+ method public android.net.Uri getLinkUri();
+ method public void releasePermission();
+ method public void requestPermission();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.inputmethod.InputContentInfo> CREATOR;
+ }
+
public abstract interface InputMethod {
method public abstract void attachToken(android.os.IBinder);
method public abstract void bindInput(android.view.inputmethod.InputBinding);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 8ccd5d2e..d6c0058 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -723,10 +723,10 @@
System.out.println("Complete");
}
mRepeat--;
- if (mRepeat > 1) {
+ if (mRepeat > 0) {
mAm.unhandledBack();
}
- } while (mRepeat > 1);
+ } while (mRepeat > 0);
}
private void runForceStop() throws Exception {
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index 7c8842c..3a92b9e 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -3,14 +3,16 @@
LOCAL_SRC_FILES:= \
bootanimation_main.cpp \
- AudioPlayer.cpp \
+ audioplay.cpp \
BootAnimation.cpp
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-LOCAL_C_INCLUDES += external/tinyalsa/include
+LOCAL_C_INCLUDES += \
+ external/tinyalsa/include \
+ frameworks/wilhelm/include
LOCAL_SHARED_LIBRARIES := \
libcutils \
@@ -23,6 +25,7 @@
libEGL \
libGLESv1_CM \
libgui \
+ libOpenSLES \
libtinyalsa
LOCAL_MODULE:= bootanimation
diff --git a/cmds/bootanimation/AudioPlayer.cpp b/cmds/bootanimation/AudioPlayer.cpp
deleted file mode 100644
index 2932130..0000000
--- a/cmds/bootanimation/AudioPlayer.cpp
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * Copyright (C) 2014 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_NDEBUG 0
-#define LOG_TAG "BootAnim_AudioPlayer"
-
-#include "AudioPlayer.h"
-
-#include <androidfw/ZipFileRO.h>
-#include <tinyalsa/asoundlib.h>
-#include <utils/Log.h>
-#include <utils/String8.h>
-
-#define ID_RIFF 0x46464952
-#define ID_WAVE 0x45564157
-#define ID_FMT 0x20746d66
-#define ID_DATA 0x61746164
-
-// Maximum line length for audio_conf.txt
-// We only accept lines less than this length to avoid overflows using sscanf()
-#define MAX_LINE_LENGTH 1024
-
-struct riff_wave_header {
- uint32_t riff_id;
- uint32_t riff_sz;
- uint32_t wave_id;
-};
-
-struct chunk_header {
- uint32_t id;
- uint32_t sz;
-};
-
-struct chunk_fmt {
- uint16_t audio_format;
- uint16_t num_channels;
- uint32_t sample_rate;
- uint32_t byte_rate;
- uint16_t block_align;
- uint16_t bits_per_sample;
-};
-
-
-namespace android {
-
-AudioPlayer::AudioPlayer()
- : mCard(-1),
- mDevice(-1),
- mPeriodSize(0),
- mPeriodCount(0),
- mCurrentFile(NULL)
-{
-}
-
-AudioPlayer::~AudioPlayer() {
-}
-
-static bool setMixerValue(struct mixer* mixer, const char* name, const char* values)
-{
- if (!mixer) {
- ALOGE("no mixer in setMixerValue");
- return false;
- }
- struct mixer_ctl *ctl = mixer_get_ctl_by_name(mixer, name);
- if (!ctl) {
- ALOGE("mixer_get_ctl_by_name failed for %s", name);
- return false;
- }
-
- enum mixer_ctl_type type = mixer_ctl_get_type(ctl);
- int numValues = mixer_ctl_get_num_values(ctl);
- int intValue;
- char stringValue[MAX_LINE_LENGTH];
-
- for (int i = 0; i < numValues && values; i++) {
- // strip leading space
- while (*values == ' ') values++;
- if (*values == 0) break;
-
- switch (type) {
- case MIXER_CTL_TYPE_BOOL:
- case MIXER_CTL_TYPE_INT:
- if (sscanf(values, "%d", &intValue) == 1) {
- if (mixer_ctl_set_value(ctl, i, intValue) != 0) {
- ALOGE("mixer_ctl_set_value failed for %s %d", name, intValue);
- }
- } else {
- ALOGE("Could not parse %s as int for %s", values, name);
- }
- break;
- case MIXER_CTL_TYPE_ENUM:
- if (sscanf(values, "%s", stringValue) == 1) {
- if (mixer_ctl_set_enum_by_string(ctl, stringValue) != 0) {
- ALOGE("mixer_ctl_set_enum_by_string failed for %s %s", name, stringValue);
- }
- } else {
- ALOGE("Could not parse %s as enum for %s", values, name);
- }
- break;
- default:
- ALOGE("unsupported mixer type %d for %s", type, name);
- break;
- }
-
- values = strchr(values, ' ');
- }
-
- return true;
-}
-
-
-/*
- * Parse the audio configuration file.
- * The file is named audio_conf.txt and must begin with the following header:
- *
- * card=<ALSA card number>
- * device=<ALSA device number>
- * period_size=<period size>
- * period_count=<period count>
- *
- * This header is followed by zero or more mixer settings, each with the format:
- * mixer "<name>" = <value list>
- * Since mixer names can contain spaces, the name must be enclosed in double quotes.
- * The values in the value list can be integers, booleans (represented by 0 or 1)
- * or strings for enum values.
- */
-bool AudioPlayer::init(const char* config)
-{
- int tempInt;
- struct mixer* mixer = NULL;
- char name[MAX_LINE_LENGTH];
-
- for (;;) {
- const char* endl = strstr(config, "\n");
- if (!endl) break;
- String8 line(config, endl - config);
- if (line.length() >= MAX_LINE_LENGTH) {
- ALOGE("Line too long in audio_conf.txt");
- return false;
- }
- const char* l = line.string();
-
- if (sscanf(l, "card=%d", &tempInt) == 1) {
- ALOGD("card=%d", tempInt);
- mCard = tempInt;
-
- mixer = mixer_open(mCard);
- if (!mixer) {
- ALOGE("could not open mixer for card %d", mCard);
- return false;
- }
- } else if (sscanf(l, "device=%d", &tempInt) == 1) {
- ALOGD("device=%d", tempInt);
- mDevice = tempInt;
- } else if (sscanf(l, "period_size=%d", &tempInt) == 1) {
- ALOGD("period_size=%d", tempInt);
- mPeriodSize = tempInt;
- } else if (sscanf(l, "period_count=%d", &tempInt) == 1) {
- ALOGD("period_count=%d", tempInt);
- mPeriodCount = tempInt;
- } else if (sscanf(l, "mixer \"%[0-9a-zA-Z _]s\"", name) == 1) {
- const char* values = strchr(l, '=');
- if (values) {
- values++; // skip '='
- ALOGD("name: \"%s\" = %s", name, values);
- setMixerValue(mixer, name, values);
- } else {
- ALOGE("values missing for name: \"%s\"", name);
- }
- }
- config = ++endl;
- }
-
- mixer_close(mixer);
-
- if (mCard >= 0 && mDevice >= 0) {
- return true;
- }
-
- return false;
-}
-
-void AudioPlayer::playFile(FileMap* fileMap) {
- // stop any currently playing sound
- requestExitAndWait();
-
- mCurrentFile = fileMap;
- run("bootanim audio", PRIORITY_URGENT_AUDIO);
-}
-
-bool AudioPlayer::threadLoop()
-{
- struct pcm_config config;
- struct pcm *pcm = NULL;
- bool moreChunks = true;
- const struct chunk_fmt* chunkFmt = NULL;
- int bufferSize;
- const uint8_t* wavData;
- size_t wavLength;
- const struct riff_wave_header* wavHeader;
-
- if (mCurrentFile == NULL) {
- ALOGE("mCurrentFile is NULL");
- return false;
- }
-
- wavData = (const uint8_t *)mCurrentFile->getDataPtr();
- if (!wavData) {
- ALOGE("Could not access WAV file data");
- goto exit;
- }
- wavLength = mCurrentFile->getDataLength();
-
- wavHeader = (const struct riff_wave_header *)wavData;
- if (wavLength < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) ||
- (wavHeader->wave_id != ID_WAVE)) {
- ALOGE("Error: audio file is not a riff/wave file\n");
- goto exit;
- }
- wavData += sizeof(*wavHeader);
- wavLength -= sizeof(*wavHeader);
-
- do {
- const struct chunk_header* chunkHeader = (const struct chunk_header*)wavData;
- if (wavLength < sizeof(*chunkHeader)) {
- ALOGE("EOF reading chunk headers");
- goto exit;
- }
-
- wavData += sizeof(*chunkHeader);
- wavLength -= sizeof(*chunkHeader);
-
- switch (chunkHeader->id) {
- case ID_FMT:
- chunkFmt = (const struct chunk_fmt *)wavData;
- wavData += chunkHeader->sz;
- wavLength -= chunkHeader->sz;
- break;
- case ID_DATA:
- /* Stop looking for chunks */
- moreChunks = 0;
- break;
- default:
- /* Unknown chunk, skip bytes */
- wavData += chunkHeader->sz;
- wavLength -= chunkHeader->sz;
- }
- } while (moreChunks);
-
- if (!chunkFmt) {
- ALOGE("format not found in WAV file");
- goto exit;
- }
-
-
- memset(&config, 0, sizeof(config));
- config.channels = chunkFmt->num_channels;
- config.rate = chunkFmt->sample_rate;
- config.period_size = mPeriodSize;
- config.period_count = mPeriodCount;
- config.start_threshold = mPeriodSize / 4;
- config.stop_threshold = INT_MAX;
- config.avail_min = config.start_threshold;
- if (chunkFmt->bits_per_sample != 16) {
- ALOGE("only 16 bit WAV files are supported");
- goto exit;
- }
- config.format = PCM_FORMAT_S16_LE;
-
- pcm = pcm_open(mCard, mDevice, PCM_OUT, &config);
- if (!pcm || !pcm_is_ready(pcm)) {
- ALOGE("Unable to open PCM device (%s)\n", pcm_get_error(pcm));
- goto exit;
- }
-
- bufferSize = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
-
- while (wavLength > 0) {
- if (exitPending()) goto exit;
- size_t count = bufferSize;
- if (count > wavLength)
- count = wavLength;
-
- if (pcm_write(pcm, wavData, count)) {
- ALOGE("pcm_write failed (%s)", pcm_get_error(pcm));
- goto exit;
- }
- wavData += count;
- wavLength -= count;
- }
-
-exit:
- if (pcm)
- pcm_close(pcm);
- delete mCurrentFile;
- mCurrentFile = NULL;
- return false;
-}
-
-} // namespace android
diff --git a/cmds/bootanimation/AudioPlayer.h b/cmds/bootanimation/AudioPlayer.h
deleted file mode 100644
index 1def0ae..0000000
--- a/cmds/bootanimation/AudioPlayer.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#ifndef _BOOTANIMATION_AUDIOPLAYER_H
-#define _BOOTANIMATION_AUDIOPLAYER_H
-
-#include <utils/Thread.h>
-#include <utils/FileMap.h>
-
-namespace android {
-
-class AudioPlayer : public Thread
-{
-public:
- AudioPlayer();
- virtual ~AudioPlayer();
- bool init(const char* config);
-
- void playFile(FileMap* fileMap);
-
-private:
- virtual bool threadLoop();
-
-private:
- int mCard; // ALSA card to use
- int mDevice; // ALSA device to use
- int mPeriodSize;
- int mPeriodCount;
-
- FileMap* mCurrentFile;
-};
-
-} // namespace android
-
-#endif // _BOOTANIMATION_AUDIOPLAYER_H
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index c597ed2..a2d34e4 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -18,6 +18,9 @@
#define LOG_TAG "BootAnimation"
#include <stdint.h>
+#include <sys/inotify.h>
+#include <sys/poll.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include <math.h>
#include <fcntl.h>
@@ -55,25 +58,45 @@
#include <EGL/eglext.h>
#include "BootAnimation.h"
-#include "AudioPlayer.h"
-
-#define OEM_BOOTANIMATION_FILE "/oem/media/bootanimation.zip"
-#define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"
-#define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip"
-#define EXIT_PROP_NAME "service.bootanim.exit"
+#include "audioplay.h"
namespace android {
+static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
+static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip";
+static const char SYSTEM_ENCRYPTED_BOOTANIMATION_FILE[] = "/system/media/bootanimation-encrypted.zip";
+static const char SYSTEM_DATA_DIR_PATH[] = "/data/system";
+static const char SYSTEM_TIME_DIR_NAME[] = "time";
+static const char SYSTEM_TIME_DIR_PATH[] = "/data/system/time";
+static const char LAST_TIME_CHANGED_FILE_NAME[] = "last_time_change";
+static const char LAST_TIME_CHANGED_FILE_PATH[] = "/data/system/time/last_time_change";
+static const char ACCURATE_TIME_FLAG_FILE_NAME[] = "time_is_accurate";
+static const char ACCURATE_TIME_FLAG_FILE_PATH[] = "/data/system/time/time_is_accurate";
+// Java timestamp format. Don't show the clock if the date is before 2000-01-01 00:00:00.
+static const long long ACCURATE_TIME_EPOCH = 946684800000;
+static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
+static const char PLAY_SOUND_PROP_NAME[] = "persist.sys.bootanim.play_sound";
static const int ANIM_ENTRY_NAME_MAX = 256;
+static const char BOOT_COMPLETED_PROP_NAME[] = "sys.boot_completed";
+static const char BOOTREASON_PROP_NAME[] = "ro.boot.bootreason";
+// bootreasons list in "system/core/bootstat/bootstat.cpp".
+static const std::vector<std::string> PLAY_SOUND_BOOTREASON_BLACKLIST {
+ "kernel_panic",
+ "Panic",
+ "Watchdog",
+};
// ---------------------------------------------------------------------------
-BootAnimation::BootAnimation() : Thread(false), mClockEnabled(true) {
+BootAnimation::BootAnimation() : Thread(false), mClockEnabled(true), mTimeIsAccurate(false),
+ mTimeCheckThread(NULL) {
mSession = new SurfaceComposerClient();
+
+ // If the system has already booted, the animation is not being used for a boot.
+ mSystemBoot = !property_get_bool(BOOT_COMPLETED_PROP_NAME, 0);
}
-BootAnimation::~BootAnimation() {
-}
+BootAnimation::~BootAnimation() {}
void BootAnimation::onFirstRef() {
status_t err = mSession->linkToComposerDeath(this);
@@ -97,9 +120,7 @@
// might be blocked on a condition variable that will never be updated.
kill( getpid(), SIGKILL );
requestExit();
- if (mAudioPlayer != NULL) {
- mAudioPlayer->requestExit();
- }
+ audioplay::destroy();
}
status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
@@ -193,25 +214,25 @@
switch (bitmap.colorType()) {
case kN32_SkColorType:
- if (tw != w || th != h) {
+ if (!mUseNpotTextures && (tw != w || th != h)) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA,
GL_UNSIGNED_BYTE, 0);
glTexSubImage2D(GL_TEXTURE_2D, 0,
0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, p);
} else {
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA,
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
GL_UNSIGNED_BYTE, p);
}
break;
case kRGB_565_SkColorType:
- if (tw != w || th != h) {
+ if (!mUseNpotTextures && (tw != w || th != h)) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB,
GL_UNSIGNED_SHORT_5_6_5, 0);
glTexSubImage2D(GL_TEXTURE_2D, 0,
0, 0, w, h, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p);
} else {
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB,
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB,
GL_UNSIGNED_SHORT_5_6_5, p);
}
break;
@@ -391,9 +412,6 @@
int exitnow = atoi(value);
if (exitnow) {
requestExit();
- if (mAudioPlayer != NULL) {
- mAudioPlayer->requestExit();
- }
}
}
@@ -515,16 +533,6 @@
}
char const* s = desString.string();
- // Create and initialize an AudioPlayer if we have an audio_conf.txt file
- String8 audioConf;
- if (readFile(animation.zip, "audio_conf.txt", audioConf)) {
- mAudioPlayer = new AudioPlayer;
- if (!mAudioPlayer->init(audioConf.string())) {
- ALOGE("mAudioPlayer.init failed");
- mAudioPlayer = NULL;
- }
- }
-
// Parse the description file
for (;;) {
const char* endl = strstr(s, "\n");
@@ -555,7 +563,7 @@
part.pause = pause;
part.path = path;
part.clockPosY = clockPosY;
- part.audioFile = NULL;
+ part.audioData = NULL;
part.animation = NULL;
if (!parseColor(color, part.backgroundColor)) {
ALOGE("> invalid color '#%s'", color);
@@ -571,7 +579,7 @@
part.playUntilComplete = false;
part.count = 1;
part.pause = 0;
- part.audioFile = NULL;
+ part.audioData = NULL;
part.animation = loadAnimation(String8(SYSTEM_BOOTANIMATION_FILE));
if (part.animation != NULL)
animation.parts.add(part);
@@ -587,15 +595,16 @@
// read all the data structures
const size_t pcount = animation.parts.size();
void *cookie = NULL;
- ZipFileRO* mZip = animation.zip;
- if (!mZip->startIteration(&cookie)) {
+ ZipFileRO* zip = animation.zip;
+ if (!zip->startIteration(&cookie)) {
return false;
}
+ Animation::Part* partWithAudio = NULL;
ZipEntryRO entry;
char name[ANIM_ENTRY_NAME_MAX];
- while ((entry = mZip->nextEntry(cookie)) != NULL) {
- const int foundEntryName = mZip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX);
+ while ((entry = zip->nextEntry(cookie)) != NULL) {
+ const int foundEntryName = zip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX);
if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) {
ALOGE("Error fetching entry file name");
continue;
@@ -605,25 +614,36 @@
const String8 path(entryName.getPathDir());
const String8 leaf(entryName.getPathLeaf());
if (leaf.size() > 0) {
- for (size_t j=0 ; j<pcount ; j++) {
+ for (size_t j = 0; j < pcount; j++) {
if (path == animation.parts[j].path) {
uint16_t method;
// supports only stored png files
- if (mZip->getEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) {
+ if (zip->getEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) {
if (method == ZipFileRO::kCompressStored) {
- FileMap* map = mZip->createEntryFileMap(entry);
+ FileMap* map = zip->createEntryFileMap(entry);
if (map) {
Animation::Part& part(animation.parts.editItemAt(j));
if (leaf == "audio.wav") {
// a part may have at most one audio file
- part.audioFile = map;
+ part.audioData = (uint8_t *)map->getDataPtr();
+ part.audioLength = map->getDataLength();
+ partWithAudio = ∂
+ } else if (leaf == "trim.txt") {
+ part.trimData.setTo((char const*)map->getDataPtr(),
+ map->getDataLength());
} else {
Animation::Frame frame;
frame.name = leaf;
frame.map = map;
+ frame.trimWidth = animation.width;
+ frame.trimHeight = animation.height;
+ frame.trimX = 0;
+ frame.trimY = 0;
part.frames.add(frame);
}
}
+ } else {
+ ALOGE("bootanimation.zip is compressed; must be only stored");
}
}
}
@@ -631,18 +651,76 @@
}
}
- mZip->endIteration(cookie);
+ // If there is trimData present, override the positioning defaults.
+ for (Animation::Part& part : animation.parts) {
+ const char* trimDataStr = part.trimData.string();
+ for (size_t frameIdx = 0; frameIdx < part.frames.size(); frameIdx++) {
+ const char* endl = strstr(trimDataStr, "\n");
+ // No more trimData for this part.
+ if (endl == NULL) {
+ break;
+ }
+ String8 line(trimDataStr, endl - trimDataStr);
+ const char* lineStr = line.string();
+ trimDataStr = ++endl;
+ int width = 0, height = 0, x = 0, y = 0;
+ if (sscanf(lineStr, "%dx%d+%d+%d", &width, &height, &x, &y) == 4) {
+ Animation::Frame& frame(part.frames.editItemAt(frameIdx));
+ frame.trimWidth = width;
+ frame.trimHeight = height;
+ frame.trimX = x;
+ frame.trimY = y;
+ } else {
+ ALOGE("Error parsing trim.txt, line: %s", lineStr);
+ break;
+ }
+ }
+ }
+
+ // Create and initialize audioplay if there is a wav file in any of the animations.
+ if (partWithAudio != NULL) {
+ ALOGD("found audio.wav, creating playback engine");
+ if (!audioplay::create(partWithAudio->audioData, partWithAudio->audioLength)) {
+ return false;
+ }
+ }
+
+ zip->endIteration(cookie);
return true;
}
bool BootAnimation::movie()
{
-
Animation* animation = loadAnimation(mZipFileName);
if (animation == NULL)
return false;
+ bool anyPartHasClock = false;
+ for (size_t i=0; i < animation->parts.size(); i++) {
+ if(animation->parts[i].clockPosY >= 0) {
+ anyPartHasClock = true;
+ break;
+ }
+ }
+ if (!anyPartHasClock) {
+ mClockEnabled = false;
+ }
+
+ // Check if npot textures are supported
+ mUseNpotTextures = false;
+ String8 gl_extensions;
+ const char* exts = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
+ if (!exts) {
+ glGetError();
+ } else {
+ gl_extensions.setTo(exts);
+ if ((gl_extensions.find("GL_ARB_texture_non_power_of_two") != -1) ||
+ (gl_extensions.find("GL_OES_texture_npot") != -1)) {
+ mUseNpotTextures = true;
+ }
+ }
+
// Blend required to draw time on top of animation frames.
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glShadeModel(GL_FLAT);
@@ -664,7 +742,18 @@
mClockEnabled = clockTextureInitialized;
}
+ if (mClockEnabled && !updateIsTimeAccurate()) {
+ mTimeCheckThread = new TimeCheckThread(this);
+ mTimeCheckThread->run("BootAnimation::TimeCheckThread", PRIORITY_NORMAL);
+ }
+
playAnimation(*animation);
+
+ if (mTimeCheckThread != NULL) {
+ mTimeCheckThread->requestExit();
+ mTimeCheckThread = NULL;
+ }
+
releaseAnimation(animation);
if (clockTextureInitialized) {
@@ -677,12 +766,9 @@
bool BootAnimation::playAnimation(const Animation& animation)
{
const size_t pcount = animation.parts.size();
- const int xc = (mWidth - animation.width) / 2;
- const int yc = ((mHeight - animation.height) / 2);
nsecs_t frameDuration = s2ns(1) / animation.fps;
-
- Region clearReg(Rect(mWidth, mHeight));
- clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height));
+ const int animationX = (mWidth - animation.width) / 2;
+ const int animationY = (mHeight - animation.height) / 2;
for (size_t i=0 ; i<pcount ; i++) {
const Animation::Part& part(animation.parts[i]);
@@ -703,8 +789,9 @@
break;
// only play audio file the first time we animate the part
- if (r == 0 && mAudioPlayer != NULL && part.audioFile) {
- mAudioPlayer->playFile(part.audioFile);
+ if (r == 0 && part.audioData && playSoundsAllowed()) {
+ ALOGD("playing clip for part%d, size=%d", (int) i, part.audioLength);
+ audioplay::playClip(part.audioData, part.audioLength);
}
glClearColor(
@@ -729,23 +816,26 @@
initTexture(frame);
}
+ const int xc = animationX + frame.trimX;
+ const int yc = animationY + frame.trimY;
+ Region clearReg(Rect(mWidth, mHeight));
+ clearReg.subtractSelf(Rect(xc, yc, xc+frame.trimWidth, yc+frame.trimHeight));
if (!clearReg.isEmpty()) {
Region::const_iterator head(clearReg.begin());
Region::const_iterator tail(clearReg.end());
glEnable(GL_SCISSOR_TEST);
while (head != tail) {
const Rect& r2(*head++);
- glScissor(r2.left, mHeight - r2.bottom,
- r2.width(), r2.height());
+ glScissor(r2.left, mHeight - r2.bottom, r2.width(), r2.height());
glClear(GL_COLOR_BUFFER_BIT);
}
glDisable(GL_SCISSOR_TEST);
}
- // specify the y center as ceiling((mHeight - animation.height) / 2)
- // which is equivalent to mHeight - (yc + animation.height)
- glDrawTexiOES(xc, mHeight - (yc + animation.height),
- 0, animation.width, animation.height);
- if (mClockEnabled && part.clockPosY >= 0) {
+ // specify the y center as ceiling((mHeight - frame.trimHeight) / 2)
+ // which is equivalent to mHeight - (yc + frame.trimHeight)
+ glDrawTexiOES(xc, mHeight - (yc + frame.trimHeight),
+ 0, frame.trimWidth, frame.trimHeight);
+ if (mClockEnabled && mTimeIsAccurate && part.clockPosY >= 0) {
drawTime(mClock, part.clockPosY);
}
@@ -776,14 +866,23 @@
break;
}
- // free the textures for this part
+ }
+
+ // Free textures created for looping parts now that the animation is done.
+ for (const Animation::Part& part : animation.parts) {
if (part.count != 1) {
- for (size_t j=0 ; j<fcount ; j++) {
+ const size_t fcount = part.frames.size();
+ for (size_t j = 0; j < fcount; j++) {
const Animation::Frame& frame(part.frames[j]);
glDeleteTextures(1, &frame.tid);
}
}
}
+
+ // we've finally played everything we're going to play
+ audioplay::setPlaying(false);
+ audioplay::destroy();
+
return true;
}
@@ -819,11 +918,165 @@
mLoadedFiles.add(animation->fileName);
parseAnimationDesc(*animation);
- preloadZip(*animation);
+ if (!preloadZip(*animation)) {
+ return NULL;
+ }
+
mLoadedFiles.remove(fn);
return animation;
}
+
+bool BootAnimation::playSoundsAllowed() const {
+ // Only play sounds for system boots, not runtime restarts.
+ if (!mSystemBoot) {
+ return false;
+ }
+
+ // Read the system property to see if we should play the sound.
+ // If it's not present, default to allowed.
+ if (!property_get_bool(PLAY_SOUND_PROP_NAME, 1)) {
+ return false;
+ }
+
+ // Don't play sounds if this is a reboot due to an error.
+ char bootreason[PROPERTY_VALUE_MAX];
+ if (property_get(BOOTREASON_PROP_NAME, bootreason, nullptr) > 0) {
+ for (const auto& str : PLAY_SOUND_BOOTREASON_BLACKLIST) {
+ if (strcasecmp(str.c_str(), bootreason) == 0) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool BootAnimation::updateIsTimeAccurate() {
+ static constexpr long long MAX_TIME_IN_PAST = 60000LL * 60LL * 24LL * 30LL; // 30 days
+ static constexpr long long MAX_TIME_IN_FUTURE = 60000LL * 90LL; // 90 minutes
+
+ if (mTimeIsAccurate) {
+ return true;
+ }
+
+ struct stat statResult;
+ if(stat(ACCURATE_TIME_FLAG_FILE_PATH, &statResult) == 0) {
+ mTimeIsAccurate = true;
+ return true;
+ }
+
+ FILE* file = fopen(LAST_TIME_CHANGED_FILE_PATH, "r");
+ if (file != NULL) {
+ long long lastChangedTime = 0;
+ fscanf(file, "%lld", &lastChangedTime);
+ fclose(file);
+ if (lastChangedTime > 0) {
+ struct timespec now;
+ clock_gettime(CLOCK_REALTIME, &now);
+ // Match the Java timestamp format
+ long long rtcNow = (now.tv_sec * 1000LL) + (now.tv_nsec / 1000000LL);
+ if (ACCURATE_TIME_EPOCH < rtcNow
+ && lastChangedTime > (rtcNow - MAX_TIME_IN_PAST)
+ && lastChangedTime < (rtcNow + MAX_TIME_IN_FUTURE)) {
+ mTimeIsAccurate = true;
+ }
+ }
+ }
+
+ return mTimeIsAccurate;
+}
+
+BootAnimation::TimeCheckThread::TimeCheckThread(BootAnimation* bootAnimation) : Thread(false),
+ mInotifyFd(-1), mSystemWd(-1), mTimeWd(-1), mBootAnimation(bootAnimation) {}
+
+BootAnimation::TimeCheckThread::~TimeCheckThread() {
+ // mInotifyFd may be -1 but that's ok since we're not at risk of attempting to close a valid FD.
+ close(mInotifyFd);
+}
+
+bool BootAnimation::TimeCheckThread::threadLoop() {
+ bool shouldLoop = doThreadLoop() && !mBootAnimation->mTimeIsAccurate
+ && mBootAnimation->mClockEnabled;
+ if (!shouldLoop) {
+ close(mInotifyFd);
+ mInotifyFd = -1;
+ }
+ return shouldLoop;
+}
+
+bool BootAnimation::TimeCheckThread::doThreadLoop() {
+ static constexpr int BUFF_LEN (10 * (sizeof(struct inotify_event) + NAME_MAX + 1));
+
+ // Poll instead of doing a blocking read so the Thread can exit if requested.
+ struct pollfd pfd = { mInotifyFd, POLLIN, 0 };
+ ssize_t pollResult = poll(&pfd, 1, 1000);
+
+ if (pollResult == 0) {
+ return true;
+ } else if (pollResult < 0) {
+ ALOGE("Could not poll inotify events");
+ return false;
+ }
+
+ char buff[BUFF_LEN] __attribute__ ((aligned(__alignof__(struct inotify_event))));;
+ ssize_t length = read(mInotifyFd, buff, BUFF_LEN);
+ if (length == 0) {
+ return true;
+ } else if (length < 0) {
+ ALOGE("Could not read inotify events");
+ return false;
+ }
+
+ const struct inotify_event *event;
+ for (char* ptr = buff; ptr < buff + length; ptr += sizeof(struct inotify_event) + event->len) {
+ event = (const struct inotify_event *) ptr;
+ if (event->wd == mSystemWd && strcmp(SYSTEM_TIME_DIR_NAME, event->name) == 0) {
+ addTimeDirWatch();
+ } else if (event->wd == mTimeWd && (strcmp(LAST_TIME_CHANGED_FILE_NAME, event->name) == 0
+ || strcmp(ACCURATE_TIME_FLAG_FILE_NAME, event->name) == 0)) {
+ return !mBootAnimation->updateIsTimeAccurate();
+ }
+ }
+
+ return true;
+}
+
+void BootAnimation::TimeCheckThread::addTimeDirWatch() {
+ mTimeWd = inotify_add_watch(mInotifyFd, SYSTEM_TIME_DIR_PATH,
+ IN_CLOSE_WRITE | IN_MOVED_TO | IN_ATTRIB);
+ if (mTimeWd > 0) {
+ // No need to watch for the time directory to be created if it already exists
+ inotify_rm_watch(mInotifyFd, mSystemWd);
+ mSystemWd = -1;
+ }
+}
+
+status_t BootAnimation::TimeCheckThread::readyToRun() {
+ mInotifyFd = inotify_init();
+ if (mInotifyFd < 0) {
+ ALOGE("Could not initialize inotify fd");
+ return NO_INIT;
+ }
+
+ mSystemWd = inotify_add_watch(mInotifyFd, SYSTEM_DATA_DIR_PATH, IN_CREATE | IN_ATTRIB);
+ if (mSystemWd < 0) {
+ close(mInotifyFd);
+ mInotifyFd = -1;
+ ALOGE("Could not add watch for %s", SYSTEM_DATA_DIR_PATH);
+ return NO_INIT;
+ }
+
+ addTimeDirWatch();
+
+ if (mBootAnimation->updateIsTimeAccurate()) {
+ close(mInotifyFd);
+ mInotifyFd = -1;
+ return ALREADY_EXISTS;
+ }
+
+ return NO_ERROR;
+}
+
// ---------------------------------------------------------------------------
}
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index d49e1ec..fd497a3 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -30,7 +30,6 @@
namespace android {
-class AudioPlayer;
class Surface;
class SurfaceComposerClient;
class SurfaceControl;
@@ -51,6 +50,24 @@
virtual void onFirstRef();
virtual void binderDied(const wp<IBinder>& who);
+ bool updateIsTimeAccurate();
+
+ class TimeCheckThread : public Thread {
+ public:
+ TimeCheckThread(BootAnimation* bootAnimation);
+ virtual ~TimeCheckThread();
+ private:
+ virtual status_t readyToRun();
+ virtual bool threadLoop();
+ bool doThreadLoop();
+ void addTimeDirWatch();
+
+ int mInotifyFd;
+ int mSystemWd;
+ int mTimeWd;
+ BootAnimation* mBootAnimation;
+ };
+
struct Texture {
GLint w;
GLint h;
@@ -61,6 +78,10 @@
struct Frame {
String8 name;
FileMap* map;
+ int trimX;
+ int trimY;
+ int trimWidth;
+ int trimHeight;
mutable GLuint tid;
bool operator < (const Frame& rhs) const {
return name < rhs.name;
@@ -72,10 +93,12 @@
int clockPosY; // The y position of the clock, in pixels, from the bottom of the
// display (the clock is centred horizontally). -1 to disable the clock
String8 path;
+ String8 trimData;
SortedVector<Frame> frames;
bool playUntilComplete;
float backgroundColor[3];
- FileMap* audioFile;
+ uint8_t* audioData;
+ int audioLength;
Animation* animation;
};
int fps;
@@ -97,24 +120,28 @@
void releaseAnimation(Animation*) const;
bool parseAnimationDesc(Animation&);
bool preloadZip(Animation &animation);
+ bool playSoundsAllowed() const;
void checkExit();
sp<SurfaceComposerClient> mSession;
- sp<AudioPlayer> mAudioPlayer;
AssetManager mAssets;
Texture mAndroid[2];
Texture mClock;
int mWidth;
int mHeight;
+ bool mUseNpotTextures = false;
EGLDisplay mDisplay;
EGLDisplay mContext;
EGLDisplay mSurface;
sp<SurfaceControl> mFlingerSurfaceControl;
sp<Surface> mFlingerSurface;
bool mClockEnabled;
+ bool mTimeIsAccurate;
+ bool mSystemBoot;
String8 mZipFileName;
SortedVector<String8> mLoadedFiles;
+ sp<TimeCheckThread> mTimeCheckThread;
};
// ---------------------------------------------------------------------------
diff --git a/cmds/bootanimation/FORMAT.md b/cmds/bootanimation/FORMAT.md
new file mode 100644
index 0000000..9ea6fea
--- /dev/null
+++ b/cmds/bootanimation/FORMAT.md
@@ -0,0 +1,101 @@
+# bootanimation format
+
+## zipfile paths
+
+The system selects a boot animation zipfile from the following locations, in order:
+
+ /system/media/bootanimation-encrypted.zip (if getprop("vold.decrypt") = '1')
+ /system/media/bootanimation.zip
+ /oem/media/bootanimation.zip
+
+## zipfile layout
+
+The `bootanimation.zip` archive file includes:
+
+ desc.txt - a text file
+ part0 \
+ part1 \ directories full of PNG frames
+ ... /
+ partN /
+
+## desc.txt format
+
+The first line defines the general parameters of the animation:
+
+ WIDTH HEIGHT FPS
+
+ * **WIDTH:** animation width (pixels)
+ * **HEIGHT:** animation height (pixels)
+ * **FPS:** frames per second, e.g. 60
+
+It is followed by a number of rows of the form:
+
+ TYPE COUNT PAUSE PATH [#RGBHEX CLOCK]
+
+ * **TYPE:** a single char indicating what type of animation segment this is:
+ + `p` -- this part will play unless interrupted by the end of the boot
+ + `c` -- this part will play to completion, no matter what
+ * **COUNT:** how many times to play the animation, or 0 to loop forever until boot is complete
+ * **PAUSE:** number of FRAMES to delay after this part ends
+ * **PATH:** directory in which to find the frames for this part (e.g. `part0`)
+ * **RGBHEX:** _(OPTIONAL)_ a background color, specified as `#RRGGBB`
+ * **CLOCK:** _(OPTIONAL)_ the y-coordinate at which to draw the current time (for watches)
+
+There is also a special TYPE, `$SYSTEM`, that loads `/system/media/bootanimation.zip`
+and plays that.
+
+## loading and playing frames
+
+Each part is scanned and loaded directly from the zip archive. Within a part directory, every file
+(except `trim.txt` and `audio.wav`; see next sections) is expected to be a PNG file that represents
+one frame in that part (at the specified resolution). For this reason it is important that frames be
+named sequentially (e.g. `part000.png`, `part001.png`, ...) and added to the zip archive in that
+order.
+
+## trim.txt
+
+To save on memory, textures may be trimmed by their background color. trim.txt sequentially lists
+the trim output for each frame in its directory, so the frames may be properly positioned.
+Output should be of the form: `WxH+X+Y`. Example:
+
+ 713x165+388+914
+ 708x152+388+912
+ 707x139+388+911
+ 649x92+388+910
+
+If the file is not present, each frame is assumed to be the same size as the animation.
+
+## audio.wav
+
+Each part may optionally play a `wav` sample when it starts. To enable this, add a file
+with the name `audio.wav` in the part directory.
+
+## exiting
+
+The system will end the boot animation (first completing any incomplete or even entirely unplayed
+parts that are of type `c`) when the system is finished booting. (This is accomplished by setting
+the system property `service.bootanim.exit` to a nonzero string.)
+
+## protips
+
+### PNG compression
+
+Use `zopflipng` if you have it, otherwise `pngcrush` will do. e.g.:
+
+ for fn in *.png ; do
+ zopflipng -m ${fn}s ${fn}s.new && mv -f ${fn}s.new ${fn}
+ # or: pngcrush -q ....
+ done
+
+Some animations benefit from being reduced to 256 colors:
+
+ pngquant --force --ext .png *.png
+ # alternatively: mogrify -colors 256 anim-tmp/*/*.png
+
+### creating the ZIP archive
+
+ cd <path-to-pieces>
+ zip -0qry -i \*.txt \*.png \*.wav @ ../bootanimation.zip *.txt part*
+
+Note that the ZIP archive is not actually compressed! The PNG files are already as compressed
+as they can reasonably get, and there is unlikely to be any redundancy between files.
diff --git a/cmds/bootanimation/audioplay.cpp b/cmds/bootanimation/audioplay.cpp
new file mode 100644
index 0000000..4983b9a
--- /dev/null
+++ b/cmds/bootanimation/audioplay.cpp
@@ -0,0 +1,386 @@
+/*
+ * 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.
+ *
+ */
+
+// cribbed from samples/native-audio
+
+#include "audioplay.h"
+
+#define CHATTY ALOGD
+#define LOG_TAG "audioplay"
+
+#include <string.h>
+
+#include <utils/Log.h>
+
+// for native audio
+#include <SLES/OpenSLES.h>
+#include <SLES/OpenSLES_Android.h>
+
+namespace audioplay {
+namespace {
+
+// engine interfaces
+static SLObjectItf engineObject = NULL;
+static SLEngineItf engineEngine;
+
+// output mix interfaces
+static SLObjectItf outputMixObject = NULL;
+
+// buffer queue player interfaces
+static SLObjectItf bqPlayerObject = NULL;
+static SLPlayItf bqPlayerPlay;
+static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
+static SLMuteSoloItf bqPlayerMuteSolo;
+static SLVolumeItf bqPlayerVolume;
+
+// pointer and size of the next player buffer to enqueue, and number of remaining buffers
+static const uint8_t* nextBuffer;
+static unsigned nextSize;
+
+static const uint32_t ID_RIFF = 0x46464952;
+static const uint32_t ID_WAVE = 0x45564157;
+static const uint32_t ID_FMT = 0x20746d66;
+static const uint32_t ID_DATA = 0x61746164;
+
+struct RiffWaveHeader {
+ uint32_t riff_id;
+ uint32_t riff_sz;
+ uint32_t wave_id;
+};
+
+struct ChunkHeader {
+ uint32_t id;
+ uint32_t sz;
+};
+
+struct ChunkFormat {
+ uint16_t audio_format;
+ uint16_t num_channels;
+ uint32_t sample_rate;
+ uint32_t byte_rate;
+ uint16_t block_align;
+ uint16_t bits_per_sample;
+};
+
+// this callback handler is called every time a buffer finishes playing
+void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
+ (void)bq;
+ (void)context;
+ audioplay::setPlaying(false);
+}
+
+bool hasPlayer() {
+ return (engineObject != NULL && bqPlayerObject != NULL);
+}
+
+// create the engine and output mix objects
+bool createEngine() {
+ SLresult result;
+
+ // create engine
+ result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
+ if (result != SL_RESULT_SUCCESS) {
+ ALOGE("slCreateEngine failed with result %d", result);
+ return false;
+ }
+ (void)result;
+
+ // realize the engine
+ result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
+ if (result != SL_RESULT_SUCCESS) {
+ ALOGE("sl engine Realize failed with result %d", result);
+ return false;
+ }
+ (void)result;
+
+ // get the engine interface, which is needed in order to create other objects
+ result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
+ if (result != SL_RESULT_SUCCESS) {
+ ALOGE("sl engine GetInterface failed with result %d", result);
+ return false;
+ }
+ (void)result;
+
+ // create output mix
+ result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);
+ if (result != SL_RESULT_SUCCESS) {
+ ALOGE("sl engine CreateOutputMix failed with result %d", result);
+ return false;
+ }
+ (void)result;
+
+ // realize the output mix
+ result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
+ if (result != SL_RESULT_SUCCESS) {
+ ALOGE("sl outputMix Realize failed with result %d", result);
+ return false;
+ }
+ (void)result;
+
+ return true;
+}
+
+// create buffer queue audio player
+bool createBufferQueueAudioPlayer(const ChunkFormat* chunkFormat) {
+ SLresult result;
+
+ // configure audio source
+ SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1};
+
+ SLDataFormat_PCM format_pcm = {
+ SL_DATAFORMAT_PCM,
+ chunkFormat->num_channels,
+ chunkFormat->sample_rate * 1000, // convert to milliHz
+ chunkFormat->bits_per_sample,
+ 16,
+ SL_SPEAKER_FRONT_CENTER,
+ SL_BYTEORDER_LITTLEENDIAN
+ };
+ SLDataSource audioSrc = {&loc_bufq, &format_pcm};
+
+ // configure audio sink
+ SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
+ SLDataSink audioSnk = {&loc_outmix, NULL};
+
+ // create audio player
+ const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_ANDROIDCONFIGURATION};
+ const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
+ result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk,
+ 3, ids, req);
+ if (result != SL_RESULT_SUCCESS) {
+ ALOGE("sl CreateAudioPlayer failed with result %d", result);
+ return false;
+ }
+ (void)result;
+
+ // Use the System stream for boot sound playback.
+ SLAndroidConfigurationItf playerConfig;
+ result = (*bqPlayerObject)->GetInterface(bqPlayerObject,
+ SL_IID_ANDROIDCONFIGURATION, &playerConfig);
+ if (result != SL_RESULT_SUCCESS) {
+ ALOGE("config GetInterface failed with result %d", result);
+ return false;
+ }
+ SLint32 streamType = SL_ANDROID_STREAM_SYSTEM;
+ result = (*playerConfig)->SetConfiguration(playerConfig,
+ SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
+ if (result != SL_RESULT_SUCCESS) {
+ ALOGE("SetConfiguration failed with result %d", result);
+ return false;
+ }
+ // use normal performance mode as low latency is not needed. This is not mandatory so
+ // do not bail if we fail
+ SLuint32 performanceMode = SL_ANDROID_PERFORMANCE_NONE;
+ result = (*playerConfig)->SetConfiguration(
+ playerConfig, SL_ANDROID_KEY_PERFORMANCE_MODE, &performanceMode, sizeof(SLuint32));
+ ALOGW_IF(result != SL_RESULT_SUCCESS,
+ "could not set performance mode on player, error %d", result);
+ (void)result;
+
+ // realize the player
+ result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
+ if (result != SL_RESULT_SUCCESS) {
+ ALOGE("sl player Realize failed with result %d", result);
+ return false;
+ }
+ (void)result;
+
+ // get the play interface
+ result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
+ if (result != SL_RESULT_SUCCESS) {
+ ALOGE("sl player GetInterface failed with result %d", result);
+ return false;
+ }
+ (void)result;
+
+ // get the buffer queue interface
+ result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
+ &bqPlayerBufferQueue);
+ if (result != SL_RESULT_SUCCESS) {
+ ALOGE("sl playberBufferQueue GetInterface failed with result %d", result);
+ return false;
+ }
+ (void)result;
+
+ // register callback on the buffer queue
+ result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
+ if (result != SL_RESULT_SUCCESS) {
+ ALOGE("sl bqPlayerBufferQueue RegisterCallback failed with result %d", result);
+ return false;
+ }
+ (void)result;
+
+ // get the volume interface
+ result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
+ if (result != SL_RESULT_SUCCESS) {
+ ALOGE("sl volume GetInterface failed with result %d", result);
+ return false;
+ }
+ (void)result;
+
+ // set the player's state to playing
+ audioplay::setPlaying(true);
+ CHATTY("Created buffer queue player: %p", bqPlayerBufferQueue);
+ return true;
+}
+
+bool parseClipBuf(const uint8_t* clipBuf, int clipBufSize, const ChunkFormat** oChunkFormat,
+ const uint8_t** oSoundBuf, unsigned* oSoundBufSize) {
+ *oSoundBuf = clipBuf;
+ *oSoundBufSize = clipBufSize;
+ *oChunkFormat = NULL;
+ const RiffWaveHeader* wavHeader = (const RiffWaveHeader*)*oSoundBuf;
+ if (*oSoundBufSize < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) ||
+ (wavHeader->wave_id != ID_WAVE)) {
+ ALOGE("Error: audio file is not a riff/wave file\n");
+ return false;
+ }
+ *oSoundBuf += sizeof(*wavHeader);
+ *oSoundBufSize -= sizeof(*wavHeader);
+
+ while (true) {
+ const ChunkHeader* chunkHeader = (const ChunkHeader*)*oSoundBuf;
+ if (*oSoundBufSize < sizeof(*chunkHeader)) {
+ ALOGE("EOF reading chunk headers");
+ return false;
+ }
+
+ *oSoundBuf += sizeof(*chunkHeader);
+ *oSoundBufSize -= sizeof(*chunkHeader);
+
+ bool endLoop = false;
+ switch (chunkHeader->id) {
+ case ID_FMT:
+ *oChunkFormat = (const ChunkFormat*)*oSoundBuf;
+ *oSoundBuf += chunkHeader->sz;
+ *oSoundBufSize -= chunkHeader->sz;
+ break;
+ case ID_DATA:
+ /* Stop looking for chunks */
+ *oSoundBufSize = chunkHeader->sz;
+ endLoop = true;
+ break;
+ default:
+ /* Unknown chunk, skip bytes */
+ *oSoundBuf += chunkHeader->sz;
+ *oSoundBufSize -= chunkHeader->sz;
+ }
+ if (endLoop) {
+ break;
+ }
+ }
+
+ if (*oChunkFormat == NULL) {
+ ALOGE("format not found in WAV file");
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+bool create(const uint8_t* exampleClipBuf, int exampleClipBufSize) {
+ if (!createEngine()) {
+ return false;
+ }
+
+ // Parse the example clip.
+ const ChunkFormat* chunkFormat;
+ const uint8_t* soundBuf;
+ unsigned soundBufSize;
+ if (!parseClipBuf(exampleClipBuf, exampleClipBufSize, &chunkFormat, &soundBuf, &soundBufSize)) {
+ return false;
+ }
+
+ // Initialize the BufferQueue based on this clip's format.
+ if (!createBufferQueueAudioPlayer(chunkFormat)) {
+ return false;
+ }
+ return true;
+}
+
+bool playClip(const uint8_t* buf, int size) {
+ // Parse the WAV header
+ const ChunkFormat* chunkFormat;
+ if (!parseClipBuf(buf, size, &chunkFormat, &nextBuffer, &nextSize)) {
+ return false;
+ }
+
+ if (!hasPlayer()) {
+ ALOGD("cannot play clip %p without a player", buf);
+ return false;
+ }
+
+ CHATTY("playClip on player %p: buf=%p size=%d nextSize %d",
+ bqPlayerBufferQueue, buf, size, nextSize);
+
+ if (nextSize > 0) {
+ // here we only enqueue one buffer because it is a long clip,
+ // but for streaming playback we would typically enqueue at least 2 buffers to start
+ SLresult result;
+ result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, nextBuffer, nextSize);
+ if (SL_RESULT_SUCCESS != result) {
+ return false;
+ }
+ audioplay::setPlaying(true);
+ }
+
+ return true;
+}
+
+// set the playing state for the buffer queue audio player
+void setPlaying(bool isPlaying) {
+ if (!hasPlayer()) return;
+
+ SLresult result;
+
+ if (NULL != bqPlayerPlay) {
+ // set the player's state
+ result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay,
+ isPlaying ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_STOPPED);
+ }
+
+}
+
+void destroy() {
+ // destroy buffer queue audio player object, and invalidate all associated interfaces
+ if (bqPlayerObject != NULL) {
+ CHATTY("destroying audio player");
+ (*bqPlayerObject)->Destroy(bqPlayerObject);
+ bqPlayerObject = NULL;
+ bqPlayerPlay = NULL;
+ bqPlayerBufferQueue = NULL;
+ bqPlayerMuteSolo = NULL;
+ bqPlayerVolume = NULL;
+ }
+
+ // destroy output mix object, and invalidate all associated interfaces
+ if (outputMixObject != NULL) {
+ (*outputMixObject)->Destroy(outputMixObject);
+ outputMixObject = NULL;
+ }
+
+ // destroy engine object, and invalidate all associated interfaces
+ if (engineObject != NULL) {
+ CHATTY("destroying audio engine");
+ (*engineObject)->Destroy(engineObject);
+ engineObject = NULL;
+ engineEngine = NULL;
+ }
+}
+
+} // namespace audioplay
diff --git a/cmds/bootanimation/audioplay.h b/cmds/bootanimation/audioplay.h
new file mode 100644
index 0000000..0e5705af
--- /dev/null
+++ b/cmds/bootanimation/audioplay.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef AUDIOPLAY_H_
+#define AUDIOPLAY_H_
+
+#include <string.h>
+
+namespace audioplay {
+
+// Initializes the engine with an example of the type of WAV clip to play.
+// All buffers passed to playClip are assumed to be in the same format.
+bool create(const uint8_t* exampleClipBuf, int exampleClipBufSize);
+
+// Plays a WAV contained in buf.
+// Should not be called while a clip is still playing.
+bool playClip(const uint8_t* buf, int size);
+void setPlaying(bool isPlaying);
+void destroy();
+
+}
+
+#endif // AUDIOPLAY_H_
diff --git a/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc
index ee0d0b8..7344ba7 100644
--- a/cmds/bootanimation/bootanim.rc
+++ b/cmds/bootanimation/bootanim.rc
@@ -4,3 +4,4 @@
group graphics audio
disabled
oneshot
+ writepid /dev/stune/top-app/tasks
\ No newline at end of file
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index c6834f9..32a8088 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -922,6 +922,8 @@
flags |= UserInfo.FLAG_EPHEMERAL;
} else if ("--guest".equals(opt)) {
flags |= UserInfo.FLAG_GUEST;
+ } else if ("--demo".equals(opt)) {
+ flags |= UserInfo.FLAG_DEMO;
} else {
System.err.println("Error: unknown option " + opt);
return showUsage();
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 7841d29..053ba7d 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -1064,15 +1064,15 @@
/**
* @hide
* TODO: For animatorSet defined in XML, we can use a flag to indicate what the play order
- * if defined (i.e. sequential or together), then we can use the flag instead of calculate
- * dynamically.
+ * if defined (i.e. sequential or together), then we can use the flag instead of calculating
+ * dynamically. Note that when AnimatorSet is empty this method returns true.
* @return whether all the animators in the set are supposed to play together
*/
public boolean shouldPlayTogether() {
updateAnimatorsDuration();
createDependencyGraph();
// All the child nodes are set out to play right after the delay animation
- return mRootNode.mChildNodes.size() == mNodes.size() - 1;
+ return mRootNode.mChildNodes == null || mRootNode.mChildNodes.size() == mNodes.size() - 1;
}
@Override
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
index 5c4b979..9a2aa30 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -977,7 +977,7 @@
@Override
void animateValue(float fraction) {
final Object target = getTarget();
- if (mTarget != null && target == null) {
+ if (target == null) {
// We lost the target reference, cancel and clean up.
cancel();
return;
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java
index 224823e..ba16e67 100644
--- a/core/java/android/animation/PropertyValuesHolder.java
+++ b/core/java/android/animation/PropertyValuesHolder.java
@@ -1095,8 +1095,12 @@
}
// TODO: We need a better way to get data out of keyframes.
if (mKeyframes instanceof PathKeyframes.FloatKeyframesBase
- || mKeyframes instanceof PathKeyframes.IntKeyframesBase) {
- // property values will animate based on external data source (e.g. Path)
+ || mKeyframes instanceof PathKeyframes.IntKeyframesBase
+ || (mKeyframes.getKeyframes() != null && mKeyframes.getKeyframes().size() > 2)) {
+ // When a pvh has more than 2 keyframes, that means there are intermediate values in
+ // addition to start/end values defined for animators. Another case where such
+ // intermediate values are defined is when animator has a path to animate along. In
+ // these cases, a data source is needed to capture these intermediate values.
values.dataSource = new PropertyValues.DataSource() {
@Override
public Object getValueAtFraction(float fraction) {
@@ -1108,6 +1112,13 @@
}
}
+ /**
+ * @hide
+ */
+ public Class getValueType() {
+ return mValueType;
+ }
+
@Override
public String toString() {
return mPropertyName + ": " + mKeyframes.toString();
@@ -1178,6 +1189,15 @@
}
@Override
+ public void setProperty(Property property) {
+ if (property instanceof IntProperty) {
+ mIntProperty = (IntProperty) property;
+ } else {
+ super.setProperty(property);
+ }
+ }
+
+ @Override
public void setIntValues(int... values) {
super.setIntValues(values);
mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes;
@@ -1316,6 +1336,15 @@
}
@Override
+ public void setProperty(Property property) {
+ if (property instanceof FloatProperty) {
+ mFloatProperty = (FloatProperty) property;
+ } else {
+ super.setProperty(property);
+ }
+ }
+
+ @Override
public void setFloatValues(float... values) {
super.setFloatValues(values);
mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
@@ -1516,7 +1545,7 @@
}
propertyMap.put(mPropertyName, mJniSetter);
}
- }
+ }
}
}
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 0c7ee2c..e3f8fa4 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -982,6 +982,7 @@
mStarted = true;
mPaused = false;
mRunning = false;
+ mAnimationEndRequested = false;
// Resets mLastFrameTime when start() is called, so that if the animation was running,
// calling start() would put the animation in the
// started-but-not-yet-reached-the-first-frame phase.
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index b1a578d..e4880b0 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4220,6 +4220,7 @@
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
+ options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
@@ -4268,6 +4269,17 @@
}
}
+ private Bundle transferSpringboardActivityOptions(Bundle options) {
+ if (options == null && (mWindow != null && !mWindow.isActive())) {
+ final ActivityOptions activityOptions = getActivityOptions();
+ if (activityOptions != null &&
+ activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
+ return activityOptions.toBundle();
+ }
+ }
+ return options;
+ }
+
/**
* @hide Implement to provide correct calling token.
*/
@@ -4283,6 +4295,7 @@
if (mParent != null) {
throw new RuntimeException("Can't be called from a child");
}
+ options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode,
options, user);
@@ -4318,6 +4331,7 @@
if (mParent != null) {
throw new RuntimeException("Can't be called from a child");
}
+ options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
@@ -4350,6 +4364,7 @@
if (mParent != null) {
throw new RuntimeException("Can't be called from a child");
}
+ options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivityAsCaller(
this, mMainThread.getApplicationThread(), mToken, this,
@@ -4789,6 +4804,7 @@
*/
public void startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent,
int requestCode, @Nullable Bundle options) {
+ options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, child,
@@ -4854,6 +4870,7 @@
if (referrer != null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
+ options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, who,
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index c96bd39e..af981f6 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -93,7 +93,8 @@
@IntDef({
BUGREPORT_OPTION_FULL,
BUGREPORT_OPTION_INTERACTIVE,
- BUGREPORT_OPTION_REMOTE
+ BUGREPORT_OPTION_REMOTE,
+ BUGREPORT_OPTION_WEAR
})
public @interface BugreportMode {}
/**
@@ -114,6 +115,11 @@
* @hide
*/
public static final int BUGREPORT_OPTION_REMOTE = 2;
+ /**
+ * Takes a bugreport on a wearable device.
+ * @hide
+ */
+ public static final int BUGREPORT_OPTION_WEAR = 3;
/**
* <a href="{@docRoot}guide/topics/manifest/meta-data-element.html">{@code
@@ -3625,6 +3631,24 @@
}
/**
+ * Enable more aggressive scheduling for latency-sensitive low-runtime VR threads. Only one
+ * thread can be a VR thread in a process at a time, and that thread may be subject to
+ * restrictions on the amount of time it can run.
+ *
+ * To reset the VR thread for an application, a tid of 0 can be passed.
+ *
+ * @see android.os.Process#myTid()
+ * @param tid tid of the VR thread
+ */
+ public static void setVrThread(int tid) {
+ try {
+ ActivityManagerNative.getDefault().setVrThread(tid);
+ } catch (RemoteException e) {
+ // pass
+ }
+ }
+
+ /**
* The AppTask allows you to manage your own application's tasks.
* See {@link android.app.ActivityManager#getAppTasks()}
*/
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 3a70a4c..0ba937a 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -19,6 +19,9 @@
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
import android.os.IBinder;
import android.service.voice.IVoiceInteractionSession;
@@ -151,4 +154,29 @@
* such as Power Save mode.
*/
public abstract void setPendingIntentWhitelistDuration(IIntentSender target, long duration);
+
+ /**
+ * Updates and persists the {@link Configuration} for a given user.
+ *
+ * @param values the configuration to update
+ * @param userId the user to update the configuration for
+ */
+ public abstract void updatePersistentConfigurationForUser(@NonNull Configuration values,
+ int userId);
+
+ /**
+ * Start activity {@code intents} as if {@code packageName} on user {@code userId} did it.
+ *
+ * @return error codes used by {@link IActivityManager#startActivity} and its siblings.
+ */
+ public abstract int startActivitiesAsPackage(String packageName,
+ int userId, Intent[] intents, Bundle bOptions);
+
+ /**
+ * Get the procstate for the UID. The return value will be between
+ * {@link ActivityManager#MIN_PROCESS_STATE} and {@link ActivityManager#MAX_PROCESS_STATE}.
+ * Note if the UID doesn't exist, it'll return {@link ActivityManager#PROCESS_STATE_NONEXISTENT}
+ * (-1).
+ */
+ public abstract int getUidProcessState(int uid);
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index dcac633..aacd5da 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2371,7 +2371,8 @@
data.enforceInterface(IActivityManager.descriptor);
IUserSwitchObserver observer = IUserSwitchObserver.Stub.asInterface(
data.readStrongBinder());
- registerUserSwitchObserver(observer);
+ String name = data.readString();
+ registerUserSwitchObserver(observer, name);
reply.writeNoException();
return true;
}
@@ -2995,6 +2996,27 @@
reply.writeInt(result);
return true;
}
+ case SET_VR_THREAD_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final int tid = data.readInt();
+ setVrThread(tid);
+ reply.writeNoException();
+ return true;
+ }
+ case SET_RENDER_THREAD_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final int tid = data.readInt();
+ setRenderThread(tid);
+ reply.writeNoException();
+ return true;
+ }
+ case SET_HAS_TOP_UI: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final boolean hasTopUi = data.readInt() != 0;
+ setHasTopUi(hasTopUi);
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -6060,11 +6082,13 @@
return result;
}
- public void registerUserSwitchObserver(IUserSwitchObserver observer) throws RemoteException {
+ public void registerUserSwitchObserver(IUserSwitchObserver observer,
+ String name) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(observer != null ? observer.asBinder() : null);
+ data.writeString(name);
mRemote.transact(REGISTER_USER_SWITCH_OBSERVER_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
@@ -7028,5 +7052,45 @@
return res;
}
+ @Override
+ public void setVrThread(int tid)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(tid);
+ mRemote.transact(SET_VR_THREAD_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ return;
+ }
+
+ public void setRenderThread(int tid)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(tid);
+ mRemote.transact(SET_RENDER_THREAD_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ return;
+ }
+
+ public void setHasTopUi(boolean hasTopUi)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(hasTopUi ? 1 : 0);
+ mRemote.transact(SET_HAS_TOP_UI, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ return;
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 4c8ddc7..d9a4690 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -31,10 +31,13 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.transition.Transition;
+import android.transition.TransitionManager;
import android.util.Pair;
import android.util.Slog;
import android.view.AppTransitionAnimationSpec;
import android.view.View;
+import android.view.ViewGroup;
import android.view.Window;
import java.util.ArrayList;
@@ -190,6 +193,7 @@
= "android:activity.exitCoordinatorIndex";
private static final String KEY_USAGE_TIME_REPORT = "android:activity.usageTimeReport";
+ private static final String KEY_ROTATION_ANIMATION_HINT = "android:activity.rotationAnimationHint";
/** @hide */
public static final int ANIM_NONE = 0;
@@ -241,6 +245,7 @@
private int mDockCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
private boolean mTaskOverlay;
private AppTransitionAnimationSpec mAnimSpecs[];
+ private int mRotationAnimationHint = -1;
/**
* Create an ActivityOptions specifying a custom animation to run when
@@ -640,10 +645,71 @@
public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
Pair<View, String>... sharedElements) {
ActivityOptions opts = new ActivityOptions();
- if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
- opts.mAnimationType = ANIM_DEFAULT;
+ makeSceneTransitionAnimation(activity, activity.getWindow(), opts,
+ activity.mExitTransitionListener, sharedElements);
+ return opts;
+ }
+
+ /**
+ * Call this immediately prior to startActivity to begin a shared element transition
+ * from a non-Activity. The window must support Window.FEATURE_ACTIVITY_TRANSITIONS.
+ * The exit transition will start immediately and the shared element transition will
+ * start once the launched Activity's shared element is ready.
+ * <p>
+ * When all transitions have completed and the shared element has been transfered,
+ * the window's decor View will have its visibility set to View.GONE.
+ *
+ * @hide
+ */
+ @SafeVarargs
+ public static ActivityOptions startSharedElementAnimation(Window window,
+ Pair<View, String>... sharedElements) {
+ ActivityOptions opts = new ActivityOptions();
+ final View decorView = window.getDecorView();
+ if (decorView == null) {
return opts;
}
+ final ExitTransitionCoordinator exit =
+ makeSceneTransitionAnimation(null, window, opts, null, sharedElements);
+ if (exit != null) {
+ HideWindowListener listener = new HideWindowListener(window, exit);
+ exit.setHideSharedElementsCallback(listener);
+ exit.startExit();
+ }
+ return opts;
+ }
+
+ /**
+ * This method should be called when the {@link #startSharedElementAnimation(Window, Pair[])}
+ * animation must be stopped and the Views reset. This can happen if there was an error
+ * from startActivity or a springboard activity and the animation should stop and reset.
+ *
+ * @hide
+ */
+ public static void stopSharedElementAnimation(Window window) {
+ final View decorView = window.getDecorView();
+ if (decorView == null) {
+ return;
+ }
+ final ExitTransitionCoordinator exit = (ExitTransitionCoordinator)
+ decorView.getTag(com.android.internal.R.id.cross_task_transition);
+ if (exit != null) {
+ exit.cancelPendingTransitions();
+ decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, null);
+ TransitionManager.endTransitions((ViewGroup) decorView);
+ exit.resetViews();
+ exit.clearState();
+ decorView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ static ExitTransitionCoordinator makeSceneTransitionAnimation(Activity activity, Window window,
+ ActivityOptions opts, SharedElementCallback callback,
+ Pair<View, String>[] sharedElements) {
+ if (!window.hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
+ opts.mAnimationType = ANIM_DEFAULT;
+ return null;
+ }
opts.mAnimationType = ANIM_SCENE_TRANSITION;
ArrayList<String> names = new ArrayList<String>();
@@ -665,18 +731,22 @@
}
}
- ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, names, names,
- views, false);
+ ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, window,
+ callback, names, names, views, false);
opts.mTransitionReceiver = exit;
opts.mSharedElementNames = names;
- opts.mIsReturning = false;
- opts.mExitCoordinatorIndex =
- activity.mActivityTransitionState.addExitTransitionCoordinator(exit);
- return opts;
+ opts.mIsReturning = (activity == null);
+ if (activity == null) {
+ opts.mExitCoordinatorIndex = -1;
+ } else {
+ opts.mExitCoordinatorIndex =
+ activity.mActivityTransitionState.addExitTransitionCoordinator(exit);
+ }
+ return exit;
}
/** @hide */
- public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
+ static ActivityOptions makeSceneTransitionAnimation(Activity activity,
ExitTransitionCoordinator exitCoordinator, ArrayList<String> sharedElementNames,
int resultCode, Intent resultData) {
ActivityOptions opts = new ActivityOptions();
@@ -795,6 +865,7 @@
mAnimationFinishedListener = IRemoteCallback.Stub.asInterface(
opts.getBinder(KEY_ANIMATION_FINISHED_LISTENER));
}
+ mRotationAnimationHint = opts.getInt(KEY_ROTATION_ANIMATION_HINT);
}
/**
@@ -900,6 +971,16 @@
return mIsReturning;
}
+ /**
+ * Returns whether or not the ActivityOptions was created with
+ * {@link #startSharedElementAnimation(Window, Pair[])}.
+ *
+ * @hide
+ */
+ boolean isCrossTask() {
+ return mExitCoordinatorIndex < 0;
+ }
+
/** @hide */
public ArrayList<String> getSharedElementNames() {
return mSharedElementNames;
@@ -1138,6 +1219,7 @@
if (mAnimationFinishedListener != null) {
b.putBinder(KEY_ANIMATION_FINISHED_LISTENER, mAnimationFinishedListener.asBinder());
}
+ b.putInt(KEY_ROTATION_ANIMATION_HINT, mRotationAnimationHint);
return b;
}
@@ -1184,6 +1266,27 @@
return null;
}
+ /**
+ * Returns the rotation animation set by {@link setRotationAnimationHint} or -1
+ * if unspecified.
+ * @hide
+ */
+ public int getRotationAnimationHint() {
+ return mRotationAnimationHint;
+ }
+
+
+ /**
+ * Set a rotation animation to be used if launching the activity
+ * triggers an orientation change, or -1 to clear. See
+ * {@link android.view.WindowManager.LayoutParams} for rotation
+ * animation values.
+ * @hide
+ */
+ public void setRotationAnimationHint(int hint) {
+ mRotationAnimationHint = hint;
+ }
+
/** @hide */
@Override
public String toString() {
@@ -1191,4 +1294,65 @@
+ ", mAnimationType=" + mAnimationType + ", mStartX=" + mStartX + ", mStartY="
+ mStartY + ", mWidth=" + mWidth + ", mHeight=" + mHeight;
}
+
+ private static class HideWindowListener extends Transition.TransitionListenerAdapter
+ implements ExitTransitionCoordinator.HideSharedElementsCallback {
+ private final Window mWindow;
+ private final ExitTransitionCoordinator mExit;
+ private final boolean mWaitingForTransition;
+ private boolean mTransitionEnded;
+ private boolean mSharedElementHidden;
+ private ArrayList<View> mSharedElements;
+
+ public HideWindowListener(Window window, ExitTransitionCoordinator exit) {
+ mWindow = window;
+ mExit = exit;
+ mSharedElements = new ArrayList<>(exit.mSharedElements);
+ Transition transition = mWindow.getExitTransition();
+ if (transition != null) {
+ transition.addListener(this);
+ mWaitingForTransition = true;
+ } else {
+ mWaitingForTransition = false;
+ }
+ View decorView = mWindow.getDecorView();
+ if (decorView != null) {
+ if (decorView.getTag(com.android.internal.R.id.cross_task_transition) != null) {
+ throw new IllegalStateException(
+ "Cannot start a transition while one is running");
+ }
+ decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, exit);
+ }
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ mTransitionEnded = true;
+ hideWhenDone();
+ transition.removeListener(this);
+ }
+
+ @Override
+ public void hideSharedElements() {
+ mSharedElementHidden = true;
+ hideWhenDone();
+ }
+
+ private void hideWhenDone() {
+ if (mSharedElementHidden && (!mWaitingForTransition || mTransitionEnded)) {
+ mExit.resetViews();
+ int numSharedElements = mSharedElements.size();
+ for (int i = 0; i < numSharedElements; i++) {
+ View view = mSharedElements.get(i);
+ view.requestLayout();
+ }
+ View decorView = mWindow.getDecorView();
+ if (decorView != null) {
+ decorView.setTagInternal(
+ com.android.internal.R.id.cross_task_transition, null);
+ decorView.setVisibility(View.GONE);
+ }
+ }
+ }
+ }
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 0728bdf..2c5f881 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -236,6 +236,7 @@
boolean mSystemThread = false;
boolean mJitEnabled = false;
boolean mSomeActivitiesChanged = false;
+ boolean mUpdatingSystemConfig = false;
// These can be accessed by multiple threads; mPackages is the lock.
// XXX For now we keep around information about all packages we have
@@ -1574,7 +1575,9 @@
case CONFIGURATION_CHANGED:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged");
mCurDefaultDisplayDpi = ((Configuration)msg.obj).densityDpi;
+ mUpdatingSystemConfig = true;
handleConfigurationChanged((Configuration)msg.obj, null);
+ mUpdatingSystemConfig = false;
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case CLEAN_UP_CONTEXT:
@@ -4624,12 +4627,20 @@
if ((activity == null) || (activity.mCurrentConfig == null)) {
shouldChangeConfig = true;
} else {
- // If the new config is the same as the config this Activity
- // is already running with then don't bother calling
- // onConfigurationChanged
+ // If the new config is the same as the config this Activity is already
+ // running with and the override config also didn't change, then don't
+ // bother calling onConfigurationChanged.
int diff = activity.mCurrentConfig.diff(newConfig);
- if (diff != 0) {
- shouldChangeConfig = true;
+ if (diff != 0 || !mResourcesManager.isSameResourcesOverrideConfig(activityToken,
+ amOverrideConfig)) {
+ // Always send the task-level config changes. For system-level configuration, if
+ // this activity doesn't handle any of the config changes, then don't bother
+ // calling onConfigurationChanged as we're going to destroy it.
+ if (!mUpdatingSystemConfig
+ || (~activity.mActivityInfo.getRealConfigChanged() & diff) == 0
+ || !reportToActivity) {
+ shouldChangeConfig = true;
+ }
}
}
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 02eb4d3..aef1d0c 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -185,7 +185,12 @@
activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
}
mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
- resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning());
+ resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning(),
+ mEnterActivityOptions.isCrossTask());
+ if (mEnterActivityOptions.isCrossTask()) {
+ mExitingFrom = new ArrayList<>(mEnterActivityOptions.getSharedElementNames());
+ mExitingTo = new ArrayList<>(mEnterActivityOptions.getSharedElementNames());
+ }
if (!mIsEnterPostponed) {
startEnter();
@@ -275,7 +280,8 @@
}
private void restoreReenteringViews() {
- if (mEnterTransitionCoordinator != null && mEnterTransitionCoordinator.isReturning()) {
+ if (mEnterTransitionCoordinator != null && mEnterTransitionCoordinator.isReturning() &&
+ !mEnterTransitionCoordinator.isCrossTask()) {
mEnterTransitionCoordinator.forceViewsToAppear();
mExitingFrom = null;
mExitingTo = null;
@@ -302,8 +308,9 @@
}
}
- mReturnExitCoordinator =
- new ExitTransitionCoordinator(activity, mEnteringNames, null, null, true);
+ mReturnExitCoordinator = new ExitTransitionCoordinator(activity,
+ activity.getWindow(), activity.mEnterTransitionListener, mEnteringNames,
+ null, null, true);
if (enterViewsTransition != null && decor != null) {
enterViewsTransition.resume(decor);
}
@@ -330,11 +337,12 @@
}
public void startExitOutTransition(Activity activity, Bundle options) {
- if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
+ mEnterTransitionCoordinator = null;
+ if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) ||
+ mExitTransitionCoordinators == null) {
return;
}
ActivityOptions activityOptions = new ActivityOptions(options);
- mEnterTransitionCoordinator = null;
if (activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
int key = activityOptions.getExitCoordinatorKey();
int index = mExitTransitionCoordinators.indexOfKey(key);
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index 7824072..9928512 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -201,7 +201,7 @@
createContextThemeWrapper);
mWindow.alwaysReadCloseOnTouchAttr();
- mAlert = new AlertController(getContext(), this, getWindow());
+ mAlert = AlertController.create(getContext(), this, getWindow());
}
static int resolveDialogTheme(Context context, int themeResId) {
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index 8692336..9fa8a5d 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -345,7 +345,7 @@
PrintWriter pw = new FastPrintWriter(sw, false, 256);
tr.printStackTrace(pw);
pw.flush();
- stackTrace = sw.toString();
+ stackTrace = sanitizeString(sw.toString());
exceptionMessage = tr.getMessage();
// Populate fields with the "root cause" exception
@@ -374,6 +374,29 @@
throwMethodName = "unknown";
throwLineNumber = 0;
}
+
+ exceptionMessage = sanitizeString(exceptionMessage);
+ }
+
+ /**
+ * Ensure that the string is of reasonable size, truncating from the middle if needed.
+ */
+ private String sanitizeString(String s) {
+ int prefixLength = 10 * 1024;
+ int suffixLength = 10 * 1024;
+ int acceptableLength = prefixLength + suffixLength;
+
+ if (s != null && s.length() > acceptableLength) {
+ String replacement =
+ "\n[TRUNCATED " + (s.length() - acceptableLength) + " CHARS]\n";
+
+ StringBuilder sb = new StringBuilder(acceptableLength + replacement.length());
+ sb.append(s.substring(0, prefixLength));
+ sb.append(replacement);
+ sb.append(s.substring(s.length() - suffixLength));
+ return sb.toString();
+ }
+ return s;
}
/**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 8cc1bc4..37faa2e 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1245,18 +1245,16 @@
return mContext.mMainThread.getSystemContext().getResources();
}
final boolean sameUid = (app.uid == Process.myUid());
- try {
- return mContext.mMainThread.getTopLevelResources(
+ final Resources r = mContext.mMainThread.getTopLevelResources(
sameUid ? app.sourceDir : app.publicSourceDir,
sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
app.resourceDirs, app.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
mContext.mPackageInfo);
- } catch (Resources.NotFoundException cause) {
- final NameNotFoundException ex =
- new NameNotFoundException("Unable to open " + app.publicSourceDir);
- ex.initCause(cause);
- throw ex;
+ if (r != null) {
+ return r;
}
+ throw new NameNotFoundException("Unable to open " + app.publicSourceDir);
+
}
@Override
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 1e2cc26..a8d332b 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -17,6 +17,7 @@
package android.app;
import android.graphics.Rect;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -852,7 +853,9 @@
* Ensure that fragments that are entering are at least at the CREATED state
* so that they may load Transitions using TransitionInflater.
*/
- if (fragment.mState < Fragment.CREATED && mManager.mCurState >= Fragment.CREATED) {
+ if (fragment.mState < Fragment.CREATED && mManager.mCurState >= Fragment.CREATED &&
+ mManager.mHost.getContext().getApplicationInfo().targetSdkVersion >=
+ Build.VERSION_CODES.N) {
mManager.makeActive(fragment);
mManager.moveToState(fragment, Fragment.CREATED, 0, 0, false);
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index e6ca520..8f42467 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -185,15 +185,6 @@
@GuardedBy("mSync")
private File mCodeCacheDir;
- @GuardedBy("mSync")
- private File[] mExternalObbDirs;
- @GuardedBy("mSync")
- private File[] mExternalFilesDirs;
- @GuardedBy("mSync")
- private File[] mExternalCacheDirs;
- @GuardedBy("mSync")
- private File[] mExternalMediaDirs;
-
// The system service cache for the system services that are cached per-ContextImpl.
final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();
@@ -562,17 +553,10 @@
@Override
public File[] getExternalFilesDirs(String type) {
synchronized (mSync) {
- if (mExternalFilesDirs == null) {
- mExternalFilesDirs = Environment.buildExternalStorageAppFilesDirs(getPackageName());
- }
-
- // Splice in requested type, if any
- File[] dirs = mExternalFilesDirs;
+ File[] dirs = Environment.buildExternalStorageAppFilesDirs(getPackageName());
if (type != null) {
dirs = Environment.buildPaths(dirs, type);
}
-
- // Create dirs if needed
return ensureExternalDirsExistOrFilter(dirs);
}
}
@@ -586,12 +570,8 @@
@Override
public File[] getObbDirs() {
synchronized (mSync) {
- if (mExternalObbDirs == null) {
- mExternalObbDirs = Environment.buildExternalStorageAppObbDirs(getPackageName());
- }
-
- // Create dirs if needed
- return ensureExternalDirsExistOrFilter(mExternalObbDirs);
+ File[] dirs = Environment.buildExternalStorageAppObbDirs(getPackageName());
+ return ensureExternalDirsExistOrFilter(dirs);
}
}
@@ -624,24 +604,16 @@
@Override
public File[] getExternalCacheDirs() {
synchronized (mSync) {
- if (mExternalCacheDirs == null) {
- mExternalCacheDirs = Environment.buildExternalStorageAppCacheDirs(getPackageName());
- }
-
- // Create dirs if needed
- return ensureExternalDirsExistOrFilter(mExternalCacheDirs);
+ File[] dirs = Environment.buildExternalStorageAppCacheDirs(getPackageName());
+ return ensureExternalDirsExistOrFilter(dirs);
}
}
@Override
public File[] getExternalMediaDirs() {
synchronized (mSync) {
- if (mExternalMediaDirs == null) {
- mExternalMediaDirs = Environment.buildExternalStorageAppMediaDirs(getPackageName());
- }
-
- // Create dirs if needed
- return ensureExternalDirsExistOrFilter(mExternalMediaDirs);
+ File[] dirs = Environment.buildExternalStorageAppMediaDirs(getPackageName());
+ return ensureExternalDirsExistOrFilter(dirs);
}
}
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 8bf1e9a..5d12b0d 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -59,12 +59,14 @@
private boolean mIsViewsTransitionStarted;
private Transition mEnterViewsTransition;
private OnPreDrawListener mViewsReadyListener;
+ private final boolean mIsCrossTask;
public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
- ArrayList<String> sharedElementNames, boolean isReturning) {
+ ArrayList<String> sharedElementNames, boolean isReturning, boolean isCrossTask) {
super(activity.getWindow(), sharedElementNames,
- getListener(activity, isReturning), isReturning);
+ getListener(activity, isReturning && !isCrossTask), isReturning);
mActivity = activity;
+ mIsCrossTask = isCrossTask;
setResultReceiver(resultReceiver);
prepareEnter();
Bundle resultReceiverBundle = new Bundle();
@@ -85,6 +87,10 @@
}
}
+ boolean isCrossTask() {
+ return mIsCrossTask;
+ }
+
public void viewInstancesReady(ArrayList<String> accepted, ArrayList<String> localNames,
ArrayList<View> localViews) {
boolean remap = false;
@@ -325,7 +331,9 @@
if (mActivity == null || decorView == null) {
return;
}
- mActivity.overridePendingTransition(0, 0);
+ if (!isCrossTask()) {
+ mActivity.overridePendingTransition(0, 0);
+ }
if (!mIsReturning) {
mWasOpaque = mActivity.convertToTranslucent(null, null);
Drawable background = decorView.getBackground();
diff --git a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl b/core/java/android/app/EphemeralResolveInfo.aidl
similarity index 94%
rename from core/java/com/android/internal/app/EphemeralResolveInfo.aidl
rename to core/java/android/app/EphemeralResolveInfo.aidl
index 529527b..db71d25 100644
--- a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl
+++ b/core/java/android/app/EphemeralResolveInfo.aidl
@@ -14,6 +14,6 @@
** limitations under the License.
*/
-package com.android.internal.app;
+package android.app;
parcelable EphemeralResolveInfo;
diff --git a/core/java/com/android/internal/app/EphemeralResolverService.java b/core/java/android/app/EphemeralResolverService.java
similarity index 69%
rename from core/java/com/android/internal/app/EphemeralResolverService.java
rename to core/java/android/app/EphemeralResolverService.java
index 6ba04a9..ba79108 100644
--- a/core/java/com/android/internal/app/EphemeralResolverService.java
+++ b/core/java/android/app/EphemeralResolverService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.app;
+package android.app;
import android.annotation.SystemApi;
import android.app.Service;
@@ -37,19 +37,24 @@
*/
@SystemApi
public abstract class EphemeralResolverService extends Service {
- public static final String EXTRA_RESOLVE_INFO = "com.android.internal.app.RESOLVE_INFO";
- public static final String EXTRA_SEQUENCE = "com.android.internal.app.SEQUENCE";
+ public static final String EXTRA_RESOLVE_INFO = "android.app.extra.RESOLVE_INFO";
+ public static final String EXTRA_SEQUENCE = "android.app.extra.SEQUENCE";
+ private static final String EXTRA_PREFIX = "android.app.PREFIX";
private Handler mHandler;
/**
* Called to retrieve resolve info for ephemeral applications.
*
* @param digestPrefix The hash prefix of the ephemeral's domain.
+ * @param prefixMask A mask that was applied to each digest prefix. This should
+ * be used when comparing against the digest prefixes as all bits might
+ * not be set.
*/
- protected abstract List<EphemeralResolveInfo> getEphemeralResolveInfoList(int digestPrefix);
+ public abstract List<EphemeralResolveInfo> onEphemeralResolveInfoList(
+ int digestPrefix[], int prefixMask);
@Override
- protected final void attachBaseContext(Context base) {
+ public final void attachBaseContext(Context base) {
super.attachBaseContext(base);
mHandler = new ServiceHandler(base.getMainLooper());
}
@@ -59,10 +64,13 @@
return new IEphemeralResolver.Stub() {
@Override
public void getEphemeralResolveInfoList(
- IRemoteCallback callback, int digestPrefix, int sequence) {
- mHandler.obtainMessage(ServiceHandler.MSG_GET_EPHEMERAL_RESOLVE_INFO,
- digestPrefix, sequence, callback)
- .sendToTarget();
+ IRemoteCallback callback, int digestPrefix[], int prefixMask, int sequence) {
+ final Message msg = mHandler.obtainMessage(
+ ServiceHandler.MSG_GET_EPHEMERAL_RESOLVE_INFO, prefixMask, sequence, callback);
+ final Bundle data = new Bundle();
+ data.putIntArray(EXTRA_PREFIX, digestPrefix);
+ msg.setData(data);
+ msg.sendToTarget();
}
};
}
@@ -81,8 +89,9 @@
switch (action) {
case MSG_GET_EPHEMERAL_RESOLVE_INFO: {
final IRemoteCallback callback = (IRemoteCallback) message.obj;
+ final int[] digestPrefix = message.getData().getIntArray(EXTRA_PREFIX);
final List<EphemeralResolveInfo> resolveInfo =
- getEphemeralResolveInfoList(message.arg1);
+ onEphemeralResolveInfoList(digestPrefix, message.arg1);
final Bundle data = new Bundle();
data.putInt(EXTRA_SEQUENCE, message.arg2);
data.putParcelableList(EXTRA_RESOLVE_INFO, resolveInfo);
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 0404288..160c285 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -35,6 +35,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
+import android.view.Window;
import java.util.ArrayList;
@@ -59,18 +60,20 @@
private Bundle mExitSharedElementBundle;
private boolean mIsExitStarted;
private boolean mSharedElementsHidden;
+ private HideSharedElementsCallback mHideSharedElementsCallback;
- public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
+ public ExitTransitionCoordinator(Activity activity, Window window,
+ SharedElementCallback listener, ArrayList<String> names,
ArrayList<String> accepted, ArrayList<View> mapped, boolean isReturning) {
- super(activity.getWindow(), names, getListener(activity, isReturning), isReturning);
+ super(window, names, listener, isReturning);
viewsReady(mapSharedElements(accepted, mapped));
stripOffscreenViews();
mIsBackgroundReady = !isReturning;
mActivity = activity;
}
- private static SharedElementCallback getListener(Activity activity, boolean isReturning) {
- return isReturning ? activity.mEnterTransitionListener : activity.mExitTransitionListener;
+ void setHideSharedElementsCallback(HideSharedElementsCallback callback) {
+ mHideSharedElementsCallback = callback;
}
@Override
@@ -188,6 +191,9 @@
private void hideSharedElements() {
moveSharedElementsFromOverlay();
+ if (mHideSharedElementsCallback != null) {
+ mHideSharedElementsCallback.hideSharedElements();
+ }
if (!mIsHidden) {
hideViews(mSharedElements);
}
@@ -207,7 +213,11 @@
startTransition(new Runnable() {
@Override
public void run() {
- beginTransitions();
+ if (mActivity != null) {
+ beginTransitions();
+ } else {
+ startExitTransition();
+ }
}
});
}
@@ -508,4 +518,8 @@
return getWindow().getSharedElementExitTransition();
}
}
+
+ interface HideSharedElementsCallback {
+ void hideSharedElements();
+ }
}
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index a637ef4..8afca78 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -1483,9 +1483,10 @@
* at this point. If you want to do work once the activity itself is
* created, see {@link #onActivityCreated(Bundle)}.
*
- * <p>If your app's <code>targetSdkVersion</code> is 23 or lower, child fragments
- * being restored from the savedInstanceState are restored after <code>onCreate</code>
- * returns. When targeting N or above and running on an N or newer platform version
+ * <p>If your app's <code>targetSdkVersion</code> is {@link android.os.Build.VERSION_CODES#M}
+ * or lower, child fragments being restored from the savedInstanceState are restored after
+ * <code>onCreate</code> returns. When targeting {@link android.os.Build.VERSION_CODES#N} or
+ * above and running on an N or newer platform version
* they are restored by <code>Fragment.onCreate</code>.</p>
*
* @param savedInstanceState If the fragment is being re-created from
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 80ba3eb..e411e03 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -512,7 +512,8 @@
public int getLaunchedFromUid(IBinder activityToken) throws RemoteException;
public String getLaunchedFromPackage(IBinder activityToken) throws RemoteException;
- public void registerUserSwitchObserver(IUserSwitchObserver observer) throws RemoteException;
+ public void registerUserSwitchObserver(IUserSwitchObserver observer,
+ String name) throws RemoteException;
public void unregisterUserSwitchObserver(IUserSwitchObserver observer) throws RemoteException;
public void requestBugReport(int bugreportType) throws RemoteException;
@@ -657,6 +658,19 @@
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options)
throws RemoteException;
+ public void setVrThread(int tid) throws RemoteException;
+ public void setRenderThread(int tid) throws RemoteException;
+
+ /**
+ * Lets activity manager know whether the calling process is currently showing "top-level" UI
+ * that is not an activity, i.e. windows on the screen the user is currently interacting with.
+ *
+ * <p>This flag can only be set for persistent processes.
+ *
+ * @param hasTopUi Whether the calling process has "top-level" UI.
+ */
+ public void setHasTopUi(boolean hasTopUi) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -1043,4 +1057,9 @@
int START_CONFIRM_DEVICE_CREDENTIAL_INTENT = IBinder.FIRST_CALL_TRANSACTION + 374;
int SEND_IDLE_JOB_TRIGGER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 375;
int SEND_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 376;
+
+ // Start of N MR1 transactions
+ int SET_VR_THREAD_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 377;
+ int SET_RENDER_THREAD_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 378;
+ int SET_HAS_TOP_UI = IBinder.FIRST_CALL_TRANSACTION + 379;
}
diff --git a/core/java/com/android/internal/app/IEphemeralResolver.aidl b/core/java/android/app/IEphemeralResolver.aidl
similarity index 88%
rename from core/java/com/android/internal/app/IEphemeralResolver.aidl
rename to core/java/android/app/IEphemeralResolver.aidl
index 40429ee..ee869ea 100644
--- a/core/java/com/android/internal/app/IEphemeralResolver.aidl
+++ b/core/java/android/app/IEphemeralResolver.aidl
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-package com.android.internal.app;
+package android.app;
-import android.content.Intent;
import android.os.IRemoteCallback;
+/** @hide */
oneway interface IEphemeralResolver {
- void getEphemeralResolveInfoList(IRemoteCallback callback, int digestPrefix, int sequence);
+ void getEphemeralResolveInfoList(IRemoteCallback callback, in int[] digestPrefix,
+ int prefixMask, int sequence);
}
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index a0762b9..75a5bf7 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -44,12 +44,12 @@
*/
ParcelFileDescriptor setWallpaper(String name, in String callingPackage,
in Rect cropHint, boolean allowBackup, out Bundle extras, int which,
- IWallpaperManagerCallback completion);
+ IWallpaperManagerCallback completion, int userId);
/**
* Set the live wallpaper. This only affects the system wallpaper.
*/
- void setWallpaperComponentChecked(in ComponentName name, in String callingPackage);
+ void setWallpaperComponentChecked(in ComponentName name, in String callingPackage, int userId);
/**
* Set the live wallpaper. This only affects the system wallpaper.
@@ -72,7 +72,7 @@
* information about that wallpaper. Otherwise, if it is a static image,
* simply return null.
*/
- WallpaperInfo getWallpaperInfo();
+ WallpaperInfo getWallpaperInfo(int userId);
/**
* Clear the system wallpaper.
@@ -128,7 +128,7 @@
/*
* Backup: is the current system wallpaper image eligible for off-device backup?
*/
- boolean isWallpaperBackupEligible(int userId);
+ boolean isWallpaperBackupEligible(int which, int userId);
/*
* Keyguard: register a callback for being notified that lock-state relevant
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index b889c8f..7754244 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1396,18 +1396,6 @@
}
public void death(ComponentName name, IBinder service) {
- ServiceDispatcher.ConnectionInfo old;
-
- synchronized (this) {
- old = mActiveConnections.remove(name);
- if (old == null || old.binder != service) {
- // Death for someone different than who we last
- // reported... just ignore it.
- return;
- }
- old.binder.unlinkToDeath(old.deathMonitor, 0);
- }
-
if (mActivityThread != null) {
mActivityThread.post(new RunConnection(name, service, 1));
} else {
@@ -1456,7 +1444,7 @@
}
}
- // If there was an old service, it is not disconnected.
+ // If there was an old service, it is now disconnected.
if (old != null) {
mConnection.onServiceDisconnected(name);
}
@@ -1467,6 +1455,17 @@
}
public void doDeath(ComponentName name, IBinder service) {
+ synchronized (this) {
+ ConnectionInfo old = mActiveConnections.get(name);
+ if (old == null || old.binder != service) {
+ // Death for someone different than who we last
+ // reported... just ignore it.
+ return;
+ }
+ mActiveConnections.remove(name);
+ old.binder.unlinkToDeath(old.deathMonitor, 0);
+ }
+
mConnection.onServiceDisconnected(name);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 35c49b3..29ed97e 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -39,7 +39,6 @@
import android.media.session.MediaSession;
import android.net.Uri;
import android.os.BadParcelableException;
-import android.os.BaseBundle;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
@@ -51,7 +50,9 @@
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.AbsoluteSizeSpan;
+import android.text.style.BackgroundColorSpan;
import android.text.style.CharacterStyle;
+import android.text.style.ForegroundColorSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.TextAppearanceSpan;
import android.util.ArraySet;
@@ -73,7 +74,6 @@
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -1307,6 +1307,7 @@
// Flags bitwise-ored to mFlags
private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
+ private static final int FLAG_HINT_DISPLAY_INLINE = 1 << 2;
// Default value for flags integer
private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
@@ -1493,6 +1494,29 @@
public boolean getHintLaunchesActivity() {
return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
}
+
+ /**
+ * Set a hint that this Action should be displayed inline.
+ *
+ * @param hintDisplayInline {@code true} if action should be displayed inline, false
+ * otherwise
+ * @return this object for method chaining
+ */
+ public WearableExtender setHintDisplayActionInline(
+ boolean hintDisplayInline) {
+ setFlag(FLAG_HINT_DISPLAY_INLINE, hintDisplayInline);
+ return this;
+ }
+
+ /**
+ * Get a hint that this Action should be displayed inline.
+ *
+ * @return {@code true} if the Action should be displayed inline, {@code false}
+ * otherwise. The default value is {@code false} if this was never set.
+ */
+ public boolean getHintDisplayActionInline() {
+ return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0;
+ }
}
}
@@ -3232,7 +3256,8 @@
* Resets the notification header to its original state
*/
private void resetNotificationHeader(RemoteViews contentView) {
- contentView.setImageViewResource(R.id.icon, 0);
+ // Small icon doesn't need to be reset, as it's always set. Resetting would prevent
+ // re-using the drawable when the notification is updated.
contentView.setBoolean(R.id.notification_header, "setExpanded", false);
contentView.setTextViewText(R.id.app_name_text, null);
contentView.setViewVisibility(R.id.chronometer, View.GONE);
@@ -3450,6 +3475,8 @@
mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
}
contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
+ contentView.setDrawableParameters(R.id.icon, false /* targetBackground */,
+ -1 /* alpha */, -1 /* colorFilter */, null /* mode */, mN.iconLevel);
processSmallIconColor(mN.mSmallIcon, contentView);
}
@@ -3495,6 +3522,8 @@
boolean validRemoteInput = false;
int N = mActions.size();
+ boolean emphazisedMode = mN.fullScreenIntent != null;
+ big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
if (N > 0) {
big.setViewVisibility(R.id.actions_container, View.VISIBLE);
big.setViewVisibility(R.id.actions, View.VISIBLE);
@@ -3505,7 +3534,8 @@
Action action = mActions.get(i);
validRemoteInput |= hasValidRemoteInput(action);
- final RemoteViews button = generateActionButton(action);
+ final RemoteViews button = generateActionButton(action, emphazisedMode,
+ i % 2 != 0);
big.addView(R.id.actions, button);
}
} else {
@@ -3670,13 +3700,13 @@
- private RemoteViews generateActionButton(Action action) {
+ private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
+ boolean oddAction) {
final boolean tombstone = (action.actionIntent == null);
RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
- tombstone ? getActionTombstoneLayoutResource()
- : getActionLayoutResource());
- final Icon ai = action.getIcon();
- button.setTextViewText(R.id.action0, processLegacyText(action.title));
+ emphazisedMode ? getEmphasizedActionLayoutResource()
+ : tombstone ? getActionTombstoneLayoutResource()
+ : getActionLayoutResource());
if (!tombstone) {
button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
}
@@ -3684,13 +3714,144 @@
if (action.mRemoteInputs != null) {
button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
}
- if (mN.color != COLOR_DEFAULT) {
- button.setTextColor(R.id.action0, resolveContrastColor());
+ if (emphazisedMode) {
+ // change the background bgColor
+ int bgColor = mContext.getColor(oddAction ? R.color.notification_action_list
+ : R.color.notification_action_list_dark);
+ button.setDrawableParameters(R.id.button_holder, true, -1, bgColor,
+ PorterDuff.Mode.SRC_ATOP, -1);
+ CharSequence title = action.title;
+ ColorStateList[] outResultColor = null;
+ if (isLegacy()) {
+ title = clearColorSpans(title);
+ } else {
+ outResultColor = new ColorStateList[1];
+ title = ensureColorSpanContrast(title, bgColor, outResultColor);
+ }
+ button.setTextViewText(R.id.action0, title);
+ if (outResultColor != null && outResultColor[0] != null) {
+ // We need to set the text color as well since changing a text to uppercase
+ // clears its spans.
+ button.setTextColor(R.id.action0, outResultColor[0]);
+ } else if (mN.color != COLOR_DEFAULT) {
+ button.setTextColor(R.id.action0,resolveContrastColor());
+ }
+ } else {
+ button.setTextViewText(R.id.action0, processLegacyText(action.title));
+ if (mN.color != COLOR_DEFAULT) {
+ button.setTextColor(R.id.action0, resolveContrastColor());
+ }
}
return button;
}
/**
+ * Clears all color spans of a text
+ * @param charSequence the input text
+ * @return the same text but without color spans
+ */
+ private CharSequence clearColorSpans(CharSequence charSequence) {
+ if (charSequence instanceof Spanned) {
+ Spanned ss = (Spanned) charSequence;
+ Object[] spans = ss.getSpans(0, ss.length(), Object.class);
+ SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
+ for (Object span : spans) {
+ Object resultSpan = span;
+ if (resultSpan instanceof CharacterStyle) {
+ resultSpan = ((CharacterStyle) span).getUnderlying();
+ }
+ if (resultSpan instanceof TextAppearanceSpan) {
+ TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
+ if (originalSpan.getTextColor() != null) {
+ resultSpan = new TextAppearanceSpan(
+ originalSpan.getFamily(),
+ originalSpan.getTextStyle(),
+ originalSpan.getTextSize(),
+ null,
+ originalSpan.getLinkTextColor());
+ }
+ } else if (resultSpan instanceof ForegroundColorSpan
+ || (resultSpan instanceof BackgroundColorSpan)) {
+ continue;
+ } else {
+ resultSpan = span;
+ }
+ builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
+ ss.getSpanFlags(span));
+ }
+ return builder;
+ }
+ return charSequence;
+ }
+
+ /**
+ * Ensures contrast on color spans against a background color. also returns the color of the
+ * text if a span was found that spans over the whole text.
+ *
+ * @param charSequence the charSequence on which the spans are
+ * @param background the background color to ensure the contrast against
+ * @param outResultColor an array in which a color will be returned as the first element if
+ * there exists a full length color span.
+ * @return the contrasted charSequence
+ */
+ private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background,
+ ColorStateList[] outResultColor) {
+ if (charSequence instanceof Spanned) {
+ Spanned ss = (Spanned) charSequence;
+ Object[] spans = ss.getSpans(0, ss.length(), Object.class);
+ SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
+ for (Object span : spans) {
+ Object resultSpan = span;
+ int spanStart = ss.getSpanStart(span);
+ int spanEnd = ss.getSpanEnd(span);
+ boolean fullLength = (spanEnd - spanStart) == charSequence.length();
+ if (resultSpan instanceof CharacterStyle) {
+ resultSpan = ((CharacterStyle) span).getUnderlying();
+ }
+ if (resultSpan instanceof TextAppearanceSpan) {
+ TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
+ ColorStateList textColor = originalSpan.getTextColor();
+ if (textColor != null) {
+ int[] colors = textColor.getColors();
+ int[] newColors = new int[colors.length];
+ for (int i = 0; i < newColors.length; i++) {
+ newColors[i] = NotificationColorUtil.ensureLargeTextContrast(
+ colors[i], background);
+ }
+ textColor = new ColorStateList(textColor.getStates().clone(),
+ newColors);
+ resultSpan = new TextAppearanceSpan(
+ originalSpan.getFamily(),
+ originalSpan.getTextStyle(),
+ originalSpan.getTextSize(),
+ textColor,
+ originalSpan.getLinkTextColor());
+ if (fullLength) {
+ outResultColor[0] = new ColorStateList(
+ textColor.getStates().clone(), newColors);
+ }
+ }
+ } else if (resultSpan instanceof ForegroundColorSpan) {
+ ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
+ int foregroundColor = originalSpan.getForegroundColor();
+ foregroundColor = NotificationColorUtil.ensureLargeTextContrast(
+ foregroundColor, background);
+ resultSpan = new ForegroundColorSpan(foregroundColor);
+ if (fullLength) {
+ outResultColor[0] = ColorStateList.valueOf(foregroundColor);
+ }
+ } else {
+ resultSpan = span;
+ }
+
+ builder.setSpan(resultSpan, spanStart, spanEnd, ss.getSpanFlags(span));
+ }
+ return builder;
+ }
+ return charSequence;
+ }
+
+ /**
* @return Whether we are currently building a notification from a legacy (an app that
* doesn't create material notifications by itself) app.
*/
@@ -3955,6 +4116,10 @@
return R.layout.notification_material_action;
}
+ private int getEmphasizedActionLayoutResource() {
+ return R.layout.notification_material_action_emphasized;
+ }
+
private int getActionTombstoneLayoutResource() {
return R.layout.notification_material_action_tombstone;
}
@@ -4260,9 +4425,15 @@
// mN.mLargeIcon
// 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Icon oldLargeIcon = null;
+ Bitmap largeIconLegacy = null;
if (mBigLargeIconSet) {
oldLargeIcon = mBuilder.mN.mLargeIcon;
mBuilder.mN.mLargeIcon = mBigLargeIcon;
+ // The legacy largeIcon might not allow us to clear the image, as it's taken in
+ // replacement if the other one is null. Because we're restoring these legacy icons
+ // for old listeners, this is in general non-null.
+ largeIconLegacy = mBuilder.mN.largeIcon;
+ mBuilder.mN.largeIcon = null;
}
RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
@@ -4274,6 +4445,7 @@
if (mBigLargeIconSet) {
mBuilder.mN.mLargeIcon = oldLargeIcon;
+ mBuilder.mN.largeIcon = largeIconLegacy;
}
contentView.setImageViewBitmap(R.id.big_picture, mPicture);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 18b72e2..ff514bd 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -312,8 +312,8 @@
try {
service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
copy, idOut, user.getIdentifier());
- if (id != idOut[0]) {
- Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
+ if (localLOGV && id != idOut[0]) {
+ Log.v(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index c4673a3..d2e0327 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -242,7 +242,7 @@
* @return a new AssetManager.
*/
@VisibleForTesting
- protected @NonNull AssetManager createAssetManager(@NonNull final ResourcesKey key) {
+ protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key) {
AssetManager assets = new AssetManager();
// resDir can be null if the 'android' package is creating a new Resources object.
@@ -250,15 +250,16 @@
// already.
if (key.mResDir != null) {
if (assets.addAssetPath(key.mResDir) == 0) {
- throw new Resources.NotFoundException("failed to add asset path " + key.mResDir);
+ Log.e(TAG, "failed to add asset path " + key.mResDir);
+ return null;
}
}
if (key.mSplitResDirs != null) {
for (final String splitResDir : key.mSplitResDirs) {
if (assets.addAssetPath(splitResDir) == 0) {
- throw new Resources.NotFoundException(
- "failed to add split asset path " + splitResDir);
+ Log.e(TAG, "failed to add split asset path " + splitResDir);
+ return null;
}
}
}
@@ -303,11 +304,15 @@
return config;
}
- private @NonNull ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key) {
+ private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key) {
final DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration);
daj.setCompatibilityInfo(key.mCompatInfo);
final AssetManager assets = createAssetManager(key);
+ if (assets == null) {
+ return null;
+ }
+
final DisplayMetrics dm = getDisplayMetrics(key.mDisplayId, daj);
final Configuration config = generateConfig(key, dm);
final ResourcesImpl impl = new ResourcesImpl(assets, dm, config, daj);
@@ -323,7 +328,7 @@
* @param key The key to match.
* @return a ResourcesImpl if the key matches a cache entry, null otherwise.
*/
- private ResourcesImpl findResourcesImplForKeyLocked(@NonNull ResourcesKey key) {
+ private @Nullable ResourcesImpl findResourcesImplForKeyLocked(@NonNull ResourcesKey key) {
WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.get(key);
ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
if (impl != null && impl.getAssets().isUpToDate()) {
@@ -338,12 +343,14 @@
* @param key The key to match.
* @return a ResourcesImpl object matching the key.
*/
- private @NonNull ResourcesImpl findOrCreateResourcesImplForKeyLocked(
+ private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked(
@NonNull ResourcesKey key) {
ResourcesImpl impl = findResourcesImplForKeyLocked(key);
if (impl == null) {
impl = createResourcesImpl(key);
- mResourceImpls.put(key, new WeakReference<>(impl));
+ if (impl != null) {
+ mResourceImpls.put(key, new WeakReference<>(impl));
+ }
}
return impl;
}
@@ -352,7 +359,8 @@
* Find the ResourcesKey that this ResourcesImpl object is associated with.
* @return the ResourcesKey or null if none was found.
*/
- private ResourcesKey findKeyForResourceImplLocked(@NonNull ResourcesImpl resourceImpl) {
+ private @Nullable ResourcesKey findKeyForResourceImplLocked(
+ @NonNull ResourcesImpl resourceImpl) {
final int refCount = mResourceImpls.size();
for (int i = 0; i < refCount; i++) {
WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
@@ -364,6 +372,26 @@
return null;
}
+ /**
+ * Check if activity resources have same override config as the provided on.
+ * @param activityToken The Activity that resources should be associated with.
+ * @param overrideConfig The override configuration to be checked for equality with.
+ * @return true if activity resources override config matches the provided one or they are both
+ * null, false otherwise.
+ */
+ boolean isSameResourcesOverrideConfig(@Nullable IBinder activityToken,
+ @Nullable Configuration overrideConfig) {
+ synchronized (this) {
+ final ActivityResources activityResources
+ = activityToken != null ? mActivityResourceReferences.get(activityToken) : null;
+ if (activityResources == null) {
+ return overrideConfig == null;
+ } else {
+ return Objects.equals(activityResources.overrideConfig, overrideConfig);
+ }
+ }
+ }
+
private ActivityResources getOrCreateActivityResourcesStructLocked(
@NonNull IBinder activityToken) {
ActivityResources activityResources = mActivityResourceReferences.get(activityToken);
@@ -460,7 +488,7 @@
* {@link ClassLoader#getSystemClassLoader()} is used.
* @return a Resources object from which to access resources.
*/
- public @NonNull Resources createBaseActivityResources(@NonNull IBinder activityToken,
+ public @Nullable Resources createBaseActivityResources(@NonNull IBinder activityToken,
@Nullable String resDir,
@Nullable String[] splitResDirs,
@Nullable String[] overlayDirs,
@@ -514,7 +542,7 @@
* {@link #applyConfigurationToResourcesLocked(Configuration, CompatibilityInfo)}
* is called.
*/
- private @NonNull Resources getOrCreateResources(@Nullable IBinder activityToken,
+ private @Nullable Resources getOrCreateResources(@Nullable IBinder activityToken,
@NonNull ResourcesKey key, @NonNull ClassLoader classLoader) {
synchronized (this) {
if (DEBUG) {
@@ -569,6 +597,9 @@
// If we're here, we didn't find a suitable ResourcesImpl to use, so create one now.
ResourcesImpl resourcesImpl = createResourcesImpl(key);
+ if (resourcesImpl == null) {
+ return null;
+ }
synchronized (this) {
ResourcesImpl existingResourcesImpl = findResourcesImplForKeyLocked(key);
@@ -622,7 +653,7 @@
* {@link ClassLoader#getSystemClassLoader()} is used.
* @return a Resources object from which to access resources.
*/
- public @NonNull Resources getResources(@Nullable IBinder activityToken,
+ public @Nullable Resources getResources(@Nullable IBinder activityToken,
@Nullable String resDir,
@Nullable String[] splitResDirs,
@Nullable String[] overlayDirs,
@@ -745,10 +776,12 @@
ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
if (resourcesImpl == null) {
resourcesImpl = createResourcesImpl(newKey);
- mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl));
+ if (resourcesImpl != null) {
+ mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl));
+ }
}
- if (resourcesImpl != resources.getImpl()) {
+ if (resourcesImpl != null && resourcesImpl != resources.getImpl()) {
// Set the ResourcesImpl, updating it for all users of this Resources
// object.
resources.setImpl(resourcesImpl);
@@ -890,7 +923,11 @@
if (r != null) {
final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
if (key != null) {
- r.setImpl(findOrCreateResourcesImplForKeyLocked(key));
+ final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
+ if (impl == null) {
+ throw new Resources.NotFoundException("failed to load " + libAsset);
+ }
+ r.setImpl(impl);
}
}
}
@@ -903,7 +940,11 @@
if (r != null) {
final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
if (key != null) {
- r.setImpl(findOrCreateResourcesImplForKeyLocked(key));
+ final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
+ if (impl == null) {
+ throw new Resources.NotFoundException("failed to load " + libAsset);
+ }
+ r.setImpl(impl);
}
}
}
diff --git a/core/java/android/app/RetailDemoModeServiceInternal.java b/core/java/android/app/RetailDemoModeServiceInternal.java
new file mode 100644
index 0000000..7ca214a
--- /dev/null
+++ b/core/java/android/app/RetailDemoModeServiceInternal.java
@@ -0,0 +1,29 @@
+/*
+ * 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.app;
+
+/**
+ * Retail Demo Mode Service interface to be used locally inside system server
+ *
+ * @hide Only for use inside system server
+ */
+public interface RetailDemoModeServiceInternal {
+ /**
+ * Used to notify RetailDemoModeService of any user activity.
+ */
+ public void onUserActivity();
+}
\ No newline at end of file
diff --git a/core/java/android/app/TabActivity.java b/core/java/android/app/TabActivity.java
index 882e55a..637c8c1 100644
--- a/core/java/android/app/TabActivity.java
+++ b/core/java/android/app/TabActivity.java
@@ -28,23 +28,6 @@
* {@link ActionBar#newTab() ActionBar.newTab()} and
* related APIs for placing tabs within their action bar area.</p>
*
- * <p>A replacement for TabActivity can also be implemented by directly using
- * TabHost. You will need to define a layout that correctly uses a TabHost
- * with a TabWidget as well as an area in which to display your tab content.
- * A typical example would be:</p>
- *
- * {@sample development/samples/Support4Demos/res/layout/fragment_tabs.xml complete}
- *
- * <p>The implementation needs to take over responsibility for switching
- * the shown content when the user switches between tabs.
- *
- * {@sample development/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabs.java
- * complete}
- *
- * <p>Also see the <a href="{@docRoot}resources/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabsPager.html">
- * Fragment Tabs Pager</a> sample for an example of using the support library's ViewPager to
- * allow the user to swipe the content to switch between tabs.</p>
- *
* @deprecated New applications should use Fragments instead of this class;
* to continue to run on older devices, you can use the v4 support library
* which provides a version of the Fragment API that is compatible down to
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index 7db9fa8..9d40381f 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -31,6 +31,7 @@
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
+import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.wallpaper.WallpaperService;
@@ -72,6 +73,10 @@
*/
final int mDescriptionResource;
+ final int mContextUriResource;
+ final int mContextDescriptionResource;
+ final boolean mShowMetadataInPreview;
+
/**
* Constructor.
*
@@ -89,7 +94,10 @@
int thumbnailRes = -1;
int authorRes = -1;
int descriptionRes = -1;
-
+ int contextUriRes = -1;
+ int contextDescriptionRes = -1;
+ boolean showMetadataInPreview = false;
+
XmlResourceParser parser = null;
try {
parser = si.loadXmlMetaData(pm, WallpaperService.SERVICE_META_DATA);
@@ -127,6 +135,15 @@
descriptionRes = sa.getResourceId(
com.android.internal.R.styleable.Wallpaper_description,
-1);
+ contextUriRes = sa.getResourceId(
+ com.android.internal.R.styleable.Wallpaper_contextUri,
+ -1);
+ contextDescriptionRes = sa.getResourceId(
+ com.android.internal.R.styleable.Wallpaper_contextDescription,
+ -1);
+ showMetadataInPreview = sa.getBoolean(
+ com.android.internal.R.styleable.Wallpaper_showMetadataInPreview,
+ false);
sa.recycle();
} catch (NameNotFoundException e) {
@@ -140,6 +157,9 @@
mThumbnailResource = thumbnailRes;
mAuthorResource = authorRes;
mDescriptionResource = descriptionRes;
+ mContextUriResource = contextUriRes;
+ mContextDescriptionResource = contextDescriptionRes;
+ mShowMetadataInPreview = showMetadataInPreview;
}
WallpaperInfo(Parcel source) {
@@ -147,6 +167,9 @@
mThumbnailResource = source.readInt();
mAuthorResource = source.readInt();
mDescriptionResource = source.readInt();
+ mContextUriResource = source.readInt();
+ mContextDescriptionResource = source.readInt();
+ mShowMetadataInPreview = source.readInt() != 0;
mService = ResolveInfo.CREATOR.createFromParcel(source);
}
@@ -248,7 +271,60 @@
return pm.getText(packageName, mDescriptionResource,
mService.serviceInfo.applicationInfo);
}
-
+
+ /**
+ * Returns an URI that specifies a link for further context about this wallpaper.
+ *
+ * @param pm An instance of {@link PackageManager} to retrieve the URI.
+ * @return The URI.
+ */
+ public Uri loadContextUri(PackageManager pm) throws NotFoundException {
+ if (mContextUriResource <= 0) throw new NotFoundException();
+ String packageName = mService.resolvePackageName;
+ ApplicationInfo applicationInfo = null;
+ if (packageName == null) {
+ packageName = mService.serviceInfo.packageName;
+ applicationInfo = mService.serviceInfo.applicationInfo;
+ }
+ String contextUriString = pm.getText(
+ packageName, mContextUriResource, applicationInfo).toString();
+ if (contextUriString == null) {
+ return null;
+ }
+ return Uri.parse(contextUriString);
+ }
+
+ /**
+ * Retrieves a title of the URI that specifies a link for further context about this wallpaper.
+ *
+ * @param pm An instance of {@link PackageManager} to retrieve the title.
+ * @return The title.
+ */
+ public CharSequence loadContextDescription(PackageManager pm) throws NotFoundException {
+ if (mContextDescriptionResource <= 0) throw new NotFoundException();
+ String packageName = mService.resolvePackageName;
+ ApplicationInfo applicationInfo = null;
+ if (packageName == null) {
+ packageName = mService.serviceInfo.packageName;
+ applicationInfo = mService.serviceInfo.applicationInfo;
+ }
+ return pm.getText(packageName, mContextDescriptionResource, applicationInfo).toString();
+ }
+
+ /**
+ * Queries whether any metadata should be shown when previewing the wallpaper. If this value is
+ * set to true, any component that shows a preview of this live wallpaper should also show
+ * accompanying information like {@link #loadLabel},
+ * {@link #loadDescription}, {@link #loadAuthor} and
+ * {@link #loadContextDescription(PackageManager)}, so the user gets to know further information
+ * about this wallpaper.
+ *
+ * @return Whether any metadata should be shown when previewing the wallpaper.
+ */
+ public boolean getShowMetadataInPreview() {
+ return mShowMetadataInPreview;
+ }
+
/**
* Return the class name of an activity that provides a settings UI for
* the wallpaper. You can launch this activity be starting it with
@@ -287,6 +363,9 @@
dest.writeInt(mThumbnailResource);
dest.writeInt(mAuthorResource);
dest.writeInt(mDescriptionResource);
+ dest.writeInt(mContextUriResource);
+ dest.writeInt(mContextDescriptionResource);
+ dest.writeInt(mShowMetadataInPreview ? 1 : 0);
mService.writeToParcel(dest, flags);
}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 7f467f0..219afea 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -48,9 +48,11 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.view.WindowManagerGlobal;
@@ -265,7 +267,7 @@
}
static class Globals extends IWallpaperManagerCallback.Stub {
- private IWallpaperManager mService;
+ private final IWallpaperManager mService;
private Bitmap mCachedWallpaper;
private int mCachedWallpaperUserId;
private Bitmap mDefaultWallpaper;
@@ -292,16 +294,16 @@
public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault,
@SetWallpaperFlags int which, int userId) {
- synchronized (this) {
- if (mService != null) {
- try {
- if (!mService.isWallpaperSupported(context.getOpPackageName())) {
- return null;
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ if (mService != null) {
+ try {
+ if (!mService.isWallpaperSupported(context.getOpPackageName())) {
+ return null;
}
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
+ }
+ synchronized (this) {
if (mCachedWallpaper != null && mCachedWallpaperUserId == userId) {
return mCachedWallpaper;
}
@@ -316,17 +318,21 @@
if (mCachedWallpaper != null) {
return mCachedWallpaper;
}
- if (returnDefault) {
- if (mDefaultWallpaper == null) {
- mDefaultWallpaper = getDefaultWallpaperLocked(context, which);
- }
- return mDefaultWallpaper;
- }
- return null;
}
+ if (returnDefault) {
+ Bitmap defaultWallpaper = mDefaultWallpaper;
+ if (defaultWallpaper == null) {
+ defaultWallpaper = getDefaultWallpaper(context, which);
+ synchronized (this) {
+ mDefaultWallpaper = defaultWallpaper;
+ }
+ }
+ return defaultWallpaper;
+ }
+ return null;
}
- public void forgetLoadedWallpaper() {
+ void forgetLoadedWallpaper() {
synchronized (this) {
mCachedWallpaper = null;
mCachedWallpaperUserId = 0;
@@ -361,7 +367,7 @@
return null;
}
- private Bitmap getDefaultWallpaperLocked(Context context, @SetWallpaperFlags int which) {
+ private Bitmap getDefaultWallpaper(Context context, @SetWallpaperFlags int which) {
InputStream is = openDefaultWallpaper(context, which);
if (is != null) {
try {
@@ -412,8 +418,14 @@
* This is returned as an
* abstract Drawable that you can install in a View to display whatever
* wallpaper the user has currently set.
+ * <p>
+ * This method can return null if there is no system wallpaper available, if
+ * wallpapers are not supported in the current user, or if the calling app is not
+ * permitted to access the system wallpaper.
*
- * @return Returns a Drawable object that will draw the wallpaper.
+ * @return Returns a Drawable object that will draw the system wallpaper,
+ * or {@code null} if no system wallpaper exists or if the calling application
+ * is not able to access the wallpaper.
*/
public Drawable getDrawable() {
Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM);
@@ -783,7 +795,7 @@
Log.w(TAG, "WallpaperService not running");
throw new RuntimeException(new DeadSystemException());
} else {
- return sGlobals.mService.getWallpaperInfo();
+ return sGlobals.mService.getWallpaperInfo(UserHandle.myUserId());
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -928,7 +940,8 @@
/* Set the wallpaper to the default values */
ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(
"res:" + resources.getResourceName(resid),
- mContext.getOpPackageName(), null, false, result, which, completion);
+ mContext.getOpPackageName(), null, false, result, which, completion,
+ UserHandle.myUserId());
if (fd != null) {
FileOutputStream fos = null;
boolean ok = false;
@@ -1029,6 +1042,19 @@
public int setBitmap(Bitmap fullImage, Rect visibleCropHint,
boolean allowBackup, @SetWallpaperFlags int which)
throws IOException {
+ return setBitmap(fullImage, visibleCropHint, allowBackup, which,
+ UserHandle.myUserId());
+ }
+
+ /**
+ * Like {@link #setBitmap(Bitmap, Rect, boolean, int)}, but allows to pass in an explicit user
+ * id. If the user id doesn't match the user id the process is running under, calling this
+ * requires permission {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
+ * @hide
+ */
+ public int setBitmap(Bitmap fullImage, Rect visibleCropHint,
+ boolean allowBackup, @SetWallpaperFlags int which, int userId)
+ throws IOException {
validateRect(visibleCropHint);
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
@@ -1039,7 +1065,7 @@
try {
ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null,
mContext.getOpPackageName(), visibleCropHint, allowBackup,
- result, which, completion);
+ result, which, completion, userId);
if (fd != null) {
FileOutputStream fos = null;
try {
@@ -1165,7 +1191,7 @@
try {
ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null,
mContext.getOpPackageName(), visibleCropHint, allowBackup,
- result, which, completion);
+ result, which, completion, UserHandle.myUserId());
if (fd != null) {
FileOutputStream fos = null;
try {
@@ -1399,12 +1425,26 @@
*/
@SystemApi
public boolean setWallpaperComponent(ComponentName name) {
+ return setWallpaperComponent(name, UserHandle.myUserId());
+ }
+
+ /**
+ * Set the live wallpaper.
+ *
+ * This can only be called by packages with android.permission.SET_WALLPAPER_COMPONENT
+ * permission. The caller must hold the INTERACT_ACROSS_USERS_FULL permission to change
+ * another user's wallpaper.
+ *
+ * @hide
+ */
+ public boolean setWallpaperComponent(ComponentName name, int userId) {
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
throw new RuntimeException(new DeadSystemException());
}
try {
- sGlobals.mService.setWallpaperComponentChecked(name, mContext.getOpPackageName());
+ sGlobals.mService.setWallpaperComponentChecked(name, mContext.getOpPackageName(),
+ userId);
return true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1655,13 +1695,13 @@
* Only the OS itself may use this method.
* @hide
*/
- public boolean isWallpaperBackupEligible() {
+ public boolean isWallpaperBackupEligible(int which) {
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
throw new RuntimeException(new DeadSystemException());
}
try {
- return sGlobals.mService.isWallpaperBackupEligible(mContext.getUserId());
+ return sGlobals.mService.isWallpaperBackupEligible(which, mContext.getUserId());
} catch (RemoteException e) {
Log.e(TAG, "Exception querying wallpaper backup eligibility: " + e.getMessage());
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 5305473..2a12ac8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1315,7 +1315,7 @@
/**
* Retrieve the current minimum password quality for a particular admin or all admins that set
- * retrictions on this user and its participating profiles. Restrictions on profiles that have
+ * restrictions on this user and its participating profiles. Restrictions on profiles that have
* a separate challenge are not taken into account.
*
* <p>This method can be called on the {@link DevicePolicyManager} instance
@@ -1379,7 +1379,7 @@
/**
* Retrieve the current minimum password length for a particular admin or all admins that set
- * retrictions on this user and its participating profiles. Restrictions on profiles that have
+ * restrictions on this user and its participating profiles. Restrictions on profiles that have
* a separate challenge are not taken into account.
*
* <p>This method can be called on the {@link DevicePolicyManager} instance
@@ -1442,7 +1442,7 @@
/**
* Retrieve the current number of upper case letters required in the password
- * for a particular admin or all admins that set retrictions on this user and
+ * for a particular admin or all admins that set restrictions on this user and
* its participating profiles. Restrictions on profiles that have a separate challenge
* are not taken into account.
* This is the same value as set by
@@ -1511,7 +1511,7 @@
/**
* Retrieve the current number of lower case letters required in the password
- * for a particular admin or all admins that set retrictions on this user
+ * for a particular admin or all admins that set restrictions on this user
* and its participating profiles. Restrictions on profiles that have
* a separate challenge are not taken into account.
* This is the same value as set by
@@ -1580,7 +1580,7 @@
/**
* Retrieve the current number of letters required in the password
- * for a particular admin or all admins that set retrictions on this user
+ * for a particular admin or all admins that set restrictions on this user
* and its participating profiles. Restrictions on profiles that have
* a separate challenge are not taken into account.
* This is the same value as set by
@@ -1648,7 +1648,7 @@
/**
* Retrieve the current number of numerical digits required in the password
- * for a particular admin or all admins that set retrictions on this user
+ * for a particular admin or all admins that set restrictions on this user
* and its participating profiles. Restrictions on profiles that have
* a separate challenge are not taken into account.
* This is the same value as set by
@@ -1716,7 +1716,7 @@
/**
* Retrieve the current number of symbols required in the password
- * for a particular admin or all admins that set retrictions on this user
+ * for a particular admin or all admins that set restrictions on this user
* and its participating profiles. Restrictions on profiles that have
* a separate challenge are not taken into account. This is the same value as
* set by {@link #setPasswordMinimumSymbols(ComponentName, int)}
@@ -1783,7 +1783,7 @@
/**
* Retrieve the current number of non-letter characters required in the password
- * for a particular admin or all admins that set retrictions on this user
+ * for a particular admin or all admins that set restrictions on this user
* and its participating profiles. Restrictions on profiles that have
* a separate challenge are not taken into account.
* This is the same value as set by
@@ -1915,7 +1915,7 @@
/**
* Get the current password expiration time for a particular admin or all admins that set
- * retrictions on this user and its participating profiles. Restrictions on profiles that have
+ * restrictions on this user and its participating profiles. Restrictions on profiles that have
* a separate challenge are not taken into account. If admin is {@code null}, then a composite
* of all expiration times is returned - which will be the minimum of all of them.
*
@@ -1939,7 +1939,7 @@
/**
* Retrieve the current password history length for a particular admin or all admins that
- * set retrictions on this user and its participating profiles. Restrictions on profiles that
+ * set restrictions on this user and its participating profiles. Restrictions on profiles that
* have a separate challenge are not taken into account.
*
* <p>This method can be called on the {@link DevicePolicyManager} instance
@@ -2121,7 +2121,7 @@
/**
* Retrieve the current maximum number of login attempts that are allowed before the device
- * or profile is wiped, for a particular admin or all admins that set retrictions on this user
+ * or profile is wiped, for a particular admin or all admins that set restrictions on this user
* and its participating profiles. Restrictions on profiles that have a separate challenge are
* not taken into account.
*
@@ -2262,7 +2262,7 @@
/**
* Retrieve the current maximum time to unlock for a particular admin or all admins that set
- * retrictions on this user and its participating profiles. Restrictions on profiles that have
+ * restrictions on this user and its participating profiles. Restrictions on profiles that have
* a separate challenge are not taken into account.
*
* <p>This method can be called on the {@link DevicePolicyManager} instance
@@ -3348,7 +3348,7 @@
/**
* Determine whether or not features have been disabled in keyguard either by the calling
- * admin, if specified, or all admins that set retrictions on this user and its participating
+ * admin, if specified, or all admins that set restrictions on this user and its participating
* profiles. Restrictions on profiles that have a separate challenge are not taken into account.
*
* <p>This method can be called on the {@link DevicePolicyManager} instance
@@ -6423,6 +6423,43 @@
}
}
+ /**
+ * @hide
+ * @return whether {@link android.provider.Settings.Global#DEVICE_PROVISIONED} has ever been set
+ * to 1.
+ */
+ public boolean isDeviceProvisioned() {
+ try {
+ return mService.isDeviceProvisioned();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Writes that the provisioning configuration has been applied.
+ */
+ public void setDeviceProvisioningConfigApplied() {
+ try {
+ mService.setDeviceProvisioningConfigApplied();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * @return whether the provisioning configuration has been applied.
+ */
+ public boolean isDeviceProvisioningConfigApplied() {
+ try {
+ return mService.isDeviceProvisioningConfigApplied();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
private void throwIfParentInstance(String functionName) {
if (mParentInstance) {
throw new SecurityException(functionName + " cannot be called on the parent instance");
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index ddec412..1036f04 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -301,4 +301,8 @@
boolean isUninstallInQueue(String packageName);
void uninstallPackageWithActiveAdmins(String packageName);
+
+ boolean isDeviceProvisioned();
+ boolean isDeviceProvisioningConfigApplied();
+ void setDeviceProvisioningConfigApplied();
}
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 478024d..76828ee 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -21,6 +21,8 @@
import android.content.res.XmlResourceParser;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageVolume;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
@@ -223,8 +225,12 @@
final int mFullBackupContent;
final PackageManager mPackageManager;
+ final StorageManager mStorageManager;
final String mPackageName;
+ // lazy initialized, only when needed
+ private StorageVolume[] mVolumes = null;
+
/**
* Parse out the semantic domains into the correct physical location.
*/
@@ -260,16 +266,41 @@
} else {
return null;
}
+ } else if (domainToken.startsWith(FullBackup.SHARED_PREFIX)) {
+ return sharedDomainToPath(domainToken);
}
// Not a supported location
Log.i(TAG, "Unrecognized domain " + domainToken);
return null;
- } catch (IOException e) {
+ } catch (Exception e) {
Log.i(TAG, "Error reading directory for domain: " + domainToken);
return null;
}
}
+
+ private String sharedDomainToPath(String domain) throws IOException {
+ // already known to start with SHARED_PREFIX, so we just look after that
+ final String volume = domain.substring(FullBackup.SHARED_PREFIX.length());
+ final StorageVolume[] volumes = getVolumeList();
+ final int volNum = Integer.parseInt(volume);
+ if (volNum < mVolumes.length) {
+ return volumes[volNum].getPathFile().getCanonicalPath();
+ }
+ return null;
+ }
+
+ private StorageVolume[] getVolumeList() {
+ if (mStorageManager != null) {
+ if (mVolumes == null) {
+ mVolumes = mStorageManager.getVolumeList();
+ }
+ } else {
+ Log.e(TAG, "Unable to access Storage Manager");
+ }
+ return mVolumes;
+ }
+
/**
* A map of domain -> list of canonical file names in that domain that are to be included.
* We keep track of the domain so that we can go through the file system in order later on.
@@ -283,6 +314,7 @@
BackupScheme(Context context) {
mFullBackupContent = context.getApplicationInfo().fullBackupContent;
+ mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
mPackageManager = context.getPackageManager();
mPackageName = context.getPackageName();
diff --git a/core/java/android/app/backup/WallpaperBackupHelper.java b/core/java/android/app/backup/WallpaperBackupHelper.java
index f256a95..f987468 100644
--- a/core/java/android/app/backup/WallpaperBackupHelper.java
+++ b/core/java/android/app/backup/WallpaperBackupHelper.java
@@ -42,7 +42,7 @@
// If 'true', then apply an acceptable-size heuristic at restore time, dropping back
// to the factory default wallpaper if the restored one differs "too much" from the
// device's preferred wallpaper image dimensions.
- private static final boolean REJECT_OUTSIZED_RESTORE = true;
+ private static final boolean REJECT_OUTSIZED_RESTORE = false;
// When outsized restore rejection is enabled, this is the maximum ratio between the
// source and target image heights that will be permitted. The ratio is checked both
@@ -60,6 +60,9 @@
public static final String WALLPAPER_IMAGE =
new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM),
"wallpaper").getAbsolutePath();
+ public static final String WALLPAPER_ORIG_IMAGE =
+ new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM),
+ "wallpaper_orig").getAbsolutePath();
public static final String WALLPAPER_INFO =
new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM),
"wallpaper_info.xml").getAbsolutePath();
@@ -199,7 +202,7 @@
// since it does not exist anywhere other than the private wallpaper
// file.
Slog.d(TAG, "Applying restored wallpaper image.");
- f.renameTo(new File(WALLPAPER_IMAGE));
+ f.renameTo(new File(WALLPAPER_ORIG_IMAGE));
}
}
}
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 5823abf..734bf69 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -165,6 +165,9 @@
* network restrictions for the requesting app. Note that this flag alone
* doesn't actually place your {@link JobService} in the foreground; you
* still need to post the notification yourself.
+ * <p>
+ * To use this flag, the caller must hold the
+ * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL} permission.
*
* @hide
*/
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 24647f38..a0da258 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -79,6 +79,13 @@
public static final int USER_INTERACTION = 7;
/**
+ * An event type denoting that an action equivalent to a ShortcutInfo is taken by the user.
+ *
+ * @see android.content.pm.ShortcutManager#reportShortcutUsed(String)
+ */
+ public static final int SHORTCUT_INVOCATION = 8;
+
+ /**
* {@hide}
*/
public String mPackage;
@@ -105,6 +112,13 @@
public Configuration mConfiguration;
/**
+ * ID of the shortcut.
+ * Only present for {@link #SHORTCUT_INVOCATION} event types.
+ * {@hide}
+ */
+ public String mShortcutId;
+
+ /**
* The package name of the source of this event.
*/
public String getPackageName() {
@@ -145,6 +159,16 @@
public Configuration getConfiguration() {
return mConfiguration;
}
+
+ /**
+ * Returns the ID of a {@link android.content.pm.ShortcutInfo} for this event
+ * if the event is of type {@link #SHORTCUT_INVOCATION}, otherwise it returns null.
+ *
+ * @see android.content.pm.ShortcutManager#reportShortcutUsed(String)
+ */
+ public String getShortcutId() {
+ return mShortcutId;
+ }
}
// Only used when creating the resulting events. Not used for reading/unparceling.
@@ -276,8 +300,13 @@
p.writeInt(event.mEventType);
p.writeLong(event.mTimeStamp);
- if (event.mEventType == Event.CONFIGURATION_CHANGE) {
- event.mConfiguration.writeToParcel(p, flags);
+ switch (event.mEventType) {
+ case Event.CONFIGURATION_CHANGE:
+ event.mConfiguration.writeToParcel(p, flags);
+ break;
+ case Event.SHORTCUT_INVOCATION:
+ p.writeString(event.mShortcutId);
+ break;
}
}
@@ -301,11 +330,18 @@
eventOut.mEventType = p.readInt();
eventOut.mTimeStamp = p.readLong();
- // Extract the configuration for configuration change events.
- if (eventOut.mEventType == Event.CONFIGURATION_CHANGE) {
- eventOut.mConfiguration = Configuration.CREATOR.createFromParcel(p);
- } else {
- eventOut.mConfiguration = null;
+ // Fill out the event-dependant fields.
+ eventOut.mConfiguration = null;
+ eventOut.mShortcutId = null;
+
+ switch (eventOut.mEventType) {
+ case Event.CONFIGURATION_CHANGE:
+ // Extract the configuration for configuration change events.
+ eventOut.mConfiguration = Configuration.CREATOR.createFromParcel(p);
+ break;
+ case Event.SHORTCUT_INVOCATION:
+ eventOut.mShortcutId = p.readString();
+ break;
}
}
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index b6f1567..a6f91fe 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -56,6 +56,17 @@
public abstract void reportConfigurationChange(Configuration config, int userId);
/**
+ * Reports that an action equivalent to a ShortcutInfo is taken by the user.
+ *
+ * @param packageName The package name of the shortcut publisher
+ * @param shortcutId The ID of the shortcut in question
+ * @param userId The user in which the content provider was accessed.
+ *
+ * @see android.content.pm.ShortcutManager#reportShortcutUsed(String)
+ */
+ public abstract void reportShortcutUsage(String packageName, String shortcutId, int userId);
+
+ /**
* Reports that a content provider has been accessed by a foreground app.
* @param name The authority of the content provider
* @param pkgName The package name of the content provider
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index 2d9f4a7..cd14469 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -34,7 +34,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemClock;
import android.util.DisplayMetrics;
import android.util.SparseArray;
import android.util.TypedValue;
@@ -187,19 +186,28 @@
idsToUpdate[i] = mViews.keyAt(i);
}
}
- List<RemoteViews> updatedViews;
- int[] updatedIds = new int[idsToUpdate.length];
+ List<PendingHostUpdate> updates;
try {
- updatedViews = sService.startListening(
- mCallbacks, mContextOpPackageName, mHostId, idsToUpdate, updatedIds).getList();
+ updates = sService.startListening(
+ mCallbacks, mContextOpPackageName, mHostId, idsToUpdate).getList();
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
}
- int N = updatedViews.size();
+ int N = updates.size();
for (int i = 0; i < N; i++) {
- updateAppWidgetView(updatedIds[i], updatedViews.get(i));
+ PendingHostUpdate update = updates.get(i);
+ switch (update.type) {
+ case PendingHostUpdate.TYPE_VIEWS_UPDATE:
+ updateAppWidgetView(update.appWidgetId, update.views);
+ break;
+ case PendingHostUpdate.TYPE_PROVIDER_CHANGED:
+ onProviderChanged(update.appWidgetId, update.widgetInfo);
+ break;
+ case PendingHostUpdate.TYPE_VIEW_DATA_CHANGED:
+ viewDataChanged(update.appWidgetId, update.viewId);
+ }
}
}
diff --git a/core/java/android/appwidget/PendingHostUpdate.java b/core/java/android/appwidget/PendingHostUpdate.java
new file mode 100644
index 0000000..5780319
--- /dev/null
+++ b/core/java/android/appwidget/PendingHostUpdate.java
@@ -0,0 +1,125 @@
+/*
+ * 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.appwidget;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.widget.RemoteViews;
+
+/**
+ * @hide
+ */
+public class PendingHostUpdate implements Parcelable {
+
+ static final int TYPE_VIEWS_UPDATE = 0;
+ static final int TYPE_PROVIDER_CHANGED = 1;
+ static final int TYPE_VIEW_DATA_CHANGED = 2;
+
+ final int appWidgetId;
+ final int type;
+ RemoteViews views;
+ AppWidgetProviderInfo widgetInfo;
+ int viewId;
+
+ public static PendingHostUpdate updateAppWidget(int appWidgetId, RemoteViews views) {
+ PendingHostUpdate update = new PendingHostUpdate(appWidgetId, TYPE_VIEWS_UPDATE);
+ update.views = views;
+ return update;
+ }
+
+ public static PendingHostUpdate providerChanged(int appWidgetId, AppWidgetProviderInfo info) {
+ PendingHostUpdate update = new PendingHostUpdate(appWidgetId, TYPE_PROVIDER_CHANGED);
+ update.widgetInfo = info;
+ return update;
+ }
+
+ public static PendingHostUpdate viewDataChanged(int appWidgetId, int viewId) {
+ PendingHostUpdate update = new PendingHostUpdate(appWidgetId, TYPE_VIEW_DATA_CHANGED);
+ update.viewId = viewId;
+ return update;
+ }
+
+ private PendingHostUpdate(int appWidgetId, int type) {
+ this.appWidgetId = appWidgetId;
+ this.type = type;
+ }
+
+ private PendingHostUpdate(Parcel in) {
+ appWidgetId = in.readInt();
+ type = in.readInt();
+
+ switch (type) {
+ case TYPE_VIEWS_UPDATE:
+ if (0 != in.readInt()) {
+ views = new RemoteViews(in);
+ }
+ break;
+ case TYPE_PROVIDER_CHANGED:
+ if (0 != in.readInt()) {
+ widgetInfo = new AppWidgetProviderInfo(in);
+ }
+ break;
+ case TYPE_VIEW_DATA_CHANGED:
+ viewId = in.readInt();
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(appWidgetId);
+ dest.writeInt(type);
+ switch (type) {
+ case TYPE_VIEWS_UPDATE:
+ writeNullParcelable(views, dest, flags);
+ break;
+ case TYPE_PROVIDER_CHANGED:
+ writeNullParcelable(widgetInfo, dest, flags);
+ break;
+ case TYPE_VIEW_DATA_CHANGED:
+ dest.writeInt(viewId);
+ break;
+ }
+ }
+
+ private void writeNullParcelable(Parcelable p, Parcel dest, int flags) {
+ if (p != null) {
+ dest.writeInt(1);
+ p.writeToParcel(dest, flags);
+ } else {
+ dest.writeInt(0);
+ }
+ }
+
+ /**
+ * Parcelable.Creator that instantiates PendingHostUpdate objects
+ */
+ public static final Parcelable.Creator<PendingHostUpdate> CREATOR
+ = new Parcelable.Creator<PendingHostUpdate>() {
+ public PendingHostUpdate createFromParcel(Parcel parcel) {
+ return new PendingHostUpdate(parcel);
+ }
+
+ public PendingHostUpdate[] newArray(int size) {
+ return new PendingHostUpdate[size];
+ }
+ };
+}
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index f66b5ff..353c640 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -30,8 +30,11 @@
import android.os.RemoteException;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
@@ -114,7 +117,8 @@
private Context mContext;
private ServiceListener mServiceListener;
- private IBluetoothA2dp mService;
+ private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
+ @GuardedBy("mServiceLock") private IBluetoothA2dp mService;
private BluetoothAdapter mAdapter;
final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
@@ -122,25 +126,27 @@
public void onBluetoothStateChange(boolean up) {
if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
if (!up) {
- if (VDBG) Log.d(TAG,"Unbinding service...");
- synchronized (mConnection) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG,"",re);
- }
+ if (VDBG) Log.d(TAG, "Unbinding service...");
+ try {
+ mServiceLock.writeLock().lock();
+ mService = null;
+ mContext.unbindService(mConnection);
+ } catch (Exception re) {
+ Log.e(TAG, "", re);
+ } finally {
+ mServiceLock.writeLock().unlock();
}
} else {
- synchronized (mConnection) {
- try {
- if (mService == null) {
- if (VDBG) Log.d(TAG,"Binding service...");
- doBind();
- }
- } catch (Exception re) {
- Log.e(TAG,"",re);
+ try {
+ mServiceLock.readLock().lock();
+ if (mService == null) {
+ if (VDBG) Log.d(TAG,"Binding service...");
+ doBind();
}
+ } catch (Exception re) {
+ Log.e(TAG,"",re);
+ } finally {
+ mServiceLock.readLock().unlock();
}
}
}
@@ -189,15 +195,16 @@
}
}
- synchronized (mConnection) {
+ try {
+ mServiceLock.writeLock().lock();
if (mService != null) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG,"",re);
- }
+ mService = null;
+ mContext.unbindService(mConnection);
}
+ } catch (Exception re) {
+ Log.e(TAG, "", re);
+ } finally {
+ mServiceLock.writeLock().unlock();
}
}
@@ -229,17 +236,20 @@
*/
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
- if (mService != null && isEnabled() &&
- isValidDevice(device)) {
- try {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled() &&
+ isValidDevice(device)) {
return mService.connect(device);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ } finally {
+ mServiceLock.readLock().unlock();
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
- return false;
}
/**
@@ -270,17 +280,20 @@
*/
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- if (mService != null && isEnabled() &&
- isValidDevice(device)) {
- try {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled() &&
+ isValidDevice(device)) {
return mService.disconnect(device);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ } finally {
+ mServiceLock.readLock().unlock();
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
- return false;
}
/**
@@ -288,16 +301,19 @@
*/
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- if (mService != null && isEnabled()) {
- try {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()) {
return mService.getConnectedDevices();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
}
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return new ArrayList<BluetoothDevice>();
+ } finally {
+ mServiceLock.readLock().unlock();
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
}
/**
@@ -305,16 +321,19 @@
*/
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- if (mService != null && isEnabled()) {
- try {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()) {
return mService.getDevicesMatchingConnectionStates(states);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
}
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return new ArrayList<BluetoothDevice>();
+ } finally {
+ mServiceLock.readLock().unlock();
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
}
/**
@@ -322,17 +341,20 @@
*/
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
- if (mService != null && isEnabled()
- && isValidDevice(device)) {
- try {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
return mService.getConnectionState(device);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
}
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return BluetoothProfile.STATE_DISCONNECTED;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return BluetoothProfile.STATE_DISCONNECTED;
+ } finally {
+ mServiceLock.readLock().unlock();
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
}
/**
@@ -352,21 +374,24 @@
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
- if (mService != null && isEnabled()
- && isValidDevice(device)) {
- if (priority != BluetoothProfile.PRIORITY_OFF &&
- priority != BluetoothProfile.PRIORITY_ON){
- return false;
- }
- try {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
+ if (priority != BluetoothProfile.PRIORITY_OFF &&
+ priority != BluetoothProfile.PRIORITY_ON) {
+ return false;
+ }
return mService.setPriority(device, priority);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ } finally {
+ mServiceLock.readLock().unlock();
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
- return false;
}
/**
@@ -385,17 +410,20 @@
@RequiresPermission(Manifest.permission.BLUETOOTH)
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
- if (mService != null && isEnabled()
- && isValidDevice(device)) {
- try {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
return mService.getPriority(device);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.PRIORITY_OFF;
}
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return BluetoothProfile.PRIORITY_OFF;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return BluetoothProfile.PRIORITY_OFF;
+ } finally {
+ mServiceLock.readLock().unlock();
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.PRIORITY_OFF;
}
/**
@@ -406,16 +434,19 @@
*/
public boolean isAvrcpAbsoluteVolumeSupported() {
if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
- if (mService != null && isEnabled()) {
- try {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()) {
return mService.isAvrcpAbsoluteVolumeSupported();
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
- return false;
}
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
+ return false;
+ } finally {
+ mServiceLock.readLock().unlock();
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
- return false;
}
/**
@@ -433,16 +464,17 @@
*/
public void adjustAvrcpAbsoluteVolume(int direction) {
if (DBG) Log.d(TAG, "adjustAvrcpAbsoluteVolume");
- if (mService != null && isEnabled()) {
- try {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()) {
mService.adjustAvrcpAbsoluteVolume(direction);
- return;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e);
- return;
}
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e);
+ } finally {
+ mServiceLock.readLock().unlock();
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
}
/**
@@ -453,16 +485,17 @@
*/
public void setAvrcpAbsoluteVolume(int volume) {
if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
- if (mService != null && isEnabled()) {
- try {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()) {
mService.setAvrcpAbsoluteVolume(volume);
- return;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e);
- return;
}
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e);
+ } finally {
+ mServiceLock.readLock().unlock();
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
}
/**
@@ -473,17 +506,20 @@
* @param device BluetoothDevice device
*/
public boolean isA2dpPlaying(BluetoothDevice device) {
- if (mService != null && isEnabled()
- && isValidDevice(device)) {
- try {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
return mService.isA2dpPlaying(device);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ } finally {
+ mServiceLock.readLock().unlock();
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
- return false;
}
/**
@@ -534,7 +570,12 @@
private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
- mService = IBluetoothA2dp.Stub.asInterface(service);
+ try {
+ mServiceLock.writeLock().lock();
+ mService = IBluetoothA2dp.Stub.asInterface(service);
+ } finally {
+ mServiceLock.writeLock().unlock();
+ }
if (mServiceListener != null) {
mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this);
@@ -542,7 +583,12 @@
}
public void onServiceDisconnected(ComponentName className) {
if (DBG) Log.d(TAG, "Proxy object disconnected");
- mService = null;
+ try {
+ mServiceLock.writeLock().lock();
+ mService = null;
+ } finally {
+ mServiceLock.writeLock().unlock();
+ }
if (mServiceListener != null) {
mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
}
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 09a15de..f46a3b3 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -220,6 +220,46 @@
* {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
* {@link #ACTION_AUDIO_STATE_CHANGED} intent.
*/
+
+ /**
+ * Intent used to broadcast the headset's indicator status
+ *
+ * <p>This intent will have 3 extras:
+ * <ul>
+ * <li> {@link #EXTRA_IND_ID} - The Assigned number of headset Indicator which is supported by
+ the headset ( as indicated by AT+BIND
+ command in the SLC sequence).or whose value
+ is changed (indicated by AT+BIEV command)</li>
+ * <li> {@link #EXTRA_IND_VALUE}- The updated value of headset indicator. </li>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+ * </ul>
+ * <p>{@link #EXTRA_IND_ID} is defined by Bluetooth SIG and each of the indicators are
+ * given an assigned number. Below shows the assigned number of Indicator added so far
+ * - Enhanced Safety - 1
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+ * receive.
+ * @hide
+ */
+ public static final String ACTION_HF_INDICATORS_VALUE_CHANGED =
+ "android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED";
+
+ /**
+ * A String extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
+ * intents that contains the UUID of the headset indicator (as defined by Bluetooth SIG)
+ * that is being sent.
+ * @hide
+ */
+ public static final String EXTRA_HF_INDICATORS_IND_ID =
+ "android.bluetooth.headset.extra.HF_INDICATORS_IND_ID";
+
+ /**
+ * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
+ * intents that contains the value of the Headset indicator that is being sent.
+ * @hide
+ */
+ public static final String EXTRA_HF_INDICATORS_IND_VALUE =
+ "android.bluetooth.headset.extra.HF_INDICATORS_IND_VALUE";
+
public static final int STATE_AUDIO_CONNECTED = 12;
private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100;
@@ -969,6 +1009,29 @@
return false;
}
+ /**
+ * Send Headset the BIND response from AG to report change in the status of the
+ * HF indicators to the headset
+ *
+ * @param ind_id Assigned Number of the indicator (defined by SIG)
+ * @param ind_status
+ * possible values- false-Indicator is disabled, no value changes shall be sent for this indicator
+ * true-Indicator is enabled, value changes may be sent for this indicator
+ * @hide
+ */
+ public void bindResponse(int ind_id, boolean ind_status) {
+ if (mService != null && isEnabled()) {
+ try {
+ mService.bindResponse(ind_id, ind_status);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ }
+
private final IBluetoothProfileServiceConnection mConnection
= new IBluetoothProfileServiceConnection.Stub() {
@Override
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
index 014cb22..e70c936 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -138,7 +138,7 @@
}
boolean doBind() {
- Intent intent = new Intent(IBluetoothMap.class.getName());
+ Intent intent = new Intent(IBluetoothSap.class.getName());
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index ae12c88..ec01bef 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -532,22 +532,19 @@
if(length <= mMaxTxPacketSize) {
mSocketOS.write(b, offset, length);
} else {
- int tmpOffset = offset;
- int tmpLength = mMaxTxPacketSize;
- int endIndex = offset + length;
- boolean done = false;
if(DBG) Log.w(TAG, "WARNING: Write buffer larger than L2CAP packet size!\n"
+ "Packet will be divided into SDU packets of size "
+ mMaxTxPacketSize);
- do{
+ int tmpOffset = offset;
+ int bytesToWrite = length;
+ while (bytesToWrite > 0) {
+ int tmpLength = (bytesToWrite > mMaxTxPacketSize)
+ ? mMaxTxPacketSize
+ : bytesToWrite;
mSocketOS.write(b, tmpOffset, tmpLength);
- tmpOffset += mMaxTxPacketSize;
- if((tmpOffset + mMaxTxPacketSize) > endIndex) {
- tmpLength = endIndex - tmpOffset;
- done = true;
- }
- } while(!done);
-
+ tmpOffset += tmpLength;
+ bytesToWrite -= tmpLength;
+ }
}
} else {
mSocketOS.write(b, offset, length);
diff --git a/core/java/android/bluetooth/IBluetoothHeadset.aidl b/core/java/android/bluetooth/IBluetoothHeadset.aidl
index 0bb4088..6ad442b 100755
--- a/core/java/android/bluetooth/IBluetoothHeadset.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadset.aidl
@@ -59,4 +59,5 @@
String number, int type);
boolean enableWBS();
boolean disableWBS();
+ void bindResponse(int ind_id, boolean ind_status);
}
diff --git a/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl
index 0b81ee8..2b853a3 100644
--- a/core/java/android/bluetooth/IBluetoothManager.aidl
+++ b/core/java/android/bluetooth/IBluetoothManager.aidl
@@ -37,6 +37,7 @@
boolean enable();
boolean enableNoAutoConnect();
boolean disable(boolean persist);
+ int getState();
IBluetoothGatt getBluetoothGatt();
boolean bindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy);
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index c365e9e..d9816a6 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -191,6 +191,14 @@
final Intent mIntent;
Uri mUri;
+ /** @hide */
+ public Item(Item other) {
+ mText = other.mText;
+ mHtmlText = other.mHtmlText;
+ mIntent = other.mIntent;
+ mUri = other.mUri;
+ }
+
/**
* Create an Item consisting of a single block of (possibly styled) text.
*/
@@ -816,6 +824,11 @@
return mItems.get(index);
}
+ /** @hide */
+ public void setItemAt(int index, Item item) {
+ mItems.set(index, item);
+ }
+
/**
* Prepare this {@link ClipData} to leave an app process.
*
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index c8d8920..b3320d6 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -34,6 +34,7 @@
import android.database.Cursor;
import android.database.IContentObserver;
import android.graphics.Point;
+import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -51,6 +52,7 @@
import android.util.Log;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.MimeIconUtils;
import com.android.internal.util.Preconditions;
import dalvik.system.CloseGuard;
@@ -2693,4 +2695,9 @@
public int resolveUserId(Uri uri) {
return ContentProvider.getUserIdFromUri(uri, mContext.getUserId());
}
+
+ /** @hide */
+ public Drawable getTypeDrawable(String mimeType) {
+ return MimeIconUtils.loadMimeIcon(mContext, mimeType);
+ }
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index bdf888f..3f18ea9 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3615,12 +3615,11 @@
public static final String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
/**
- * TODO Javadoc
+ * Use with {@link #getSystemService} to retrieve a
+ * {@link android.content.pm.ShortcutManager} for accessing the launcher shortcut service.
*
* @see #getSystemService
* @see android.content.pm.ShortcutManager
- *
- * @hide
*/
public static final String SHORTCUT_SERVICE = "shortcut";
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index ace54ba..4d9db98 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2219,6 +2219,22 @@
"android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE";
/**
+ * Broadcast Action: preferred activities have changed *explicitly*.
+ *
+ * <p>Note there are cases where a preferred activity is invalidated *implicitly*, e.g.
+ * when an app is installed or uninstalled, but in such cases this broadcast will *not*
+ * be sent.
+ *
+ * {@link #EXTRA_USER_HANDLE} contains the user ID in question.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PREFERRED_ACTIVITY_CHANGED =
+ "android.intent.action.ACTION_PREFERRED_ACTIVITY_CHANGED";
+
+
+ /**
* Broadcast Action: The current system wallpaper has changed. See
* {@link android.app.WallpaperManager} for retrieving the new wallpaper.
* This should <em>only</em> be used to determine when the wallpaper
@@ -3174,6 +3190,14 @@
public static final String ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR";
/**
+ * Boolean intent extra to be used with {@link ACTION_MASTER_CLEAR} in order to force a factory
+ * reset even if {@link android.os.UserManager.DISALLOW_FACTORY_RESET} is set.
+ * @hide
+ */
+ public static final String EXTRA_FORCE_MASTER_CLEAR =
+ "android.intent.extra.FORCE_MASTER_CLEAR";
+
+ /**
* Broadcast action: report that a settings element is being restored from backup. The intent
* contains three extras: EXTRA_SETTING_NAME is a string naming the restored setting,
* EXTRA_SETTING_NEW_VALUE is the value being restored, and EXTRA_SETTING_PREVIOUS_VALUE
diff --git a/core/java/android/content/pm/EphemeralResolveInfo.java b/core/java/android/content/pm/EphemeralResolveInfo.java
index afb4c30..fc3b958 100644
--- a/core/java/android/content/pm/EphemeralResolveInfo.java
+++ b/core/java/android/content/pm/EphemeralResolveInfo.java
@@ -27,6 +27,7 @@
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
/**
* Information about an ephemeral application.
@@ -37,10 +38,7 @@
/** Algorithm that will be used to generate the domain digest */
public static final String SHA_ALGORITHM = "SHA-256";
- /** Full digest of the domain hash */
- private final byte[] mDigestBytes;
- /** The first 4 bytes of the domain hash */
- private final int mDigestPrefix;
+ private final EphemeralDigest mDigest;
private final String mPackageName;
/** The filters used to match domain */
private final List<IntentFilter> mFilters = new ArrayList<IntentFilter>();
@@ -55,29 +53,23 @@
throw new IllegalArgumentException();
}
- mDigestBytes = generateDigest(uri);
- mDigestPrefix =
- mDigestBytes[0] << 24
- | mDigestBytes[1] << 16
- | mDigestBytes[2] << 8
- | mDigestBytes[3] << 0;
+ mDigest = new EphemeralDigest(uri, 0xFFFFFFFF, -1);
mFilters.addAll(filters);
mPackageName = packageName;
}
EphemeralResolveInfo(Parcel in) {
- mDigestBytes = in.createByteArray();
- mDigestPrefix = in.readInt();
+ mDigest = in.readParcelable(null /*loader*/);
mPackageName = in.readString();
in.readList(mFilters, null /*loader*/);
}
public byte[] getDigestBytes() {
- return mDigestBytes;
+ return mDigest.getDigestBytes()[0];
}
public int getDigestPrefix() {
- return mDigestPrefix;
+ return mDigest.getDigestPrefix()[0];
}
public String getPackageName() {
@@ -88,16 +80,6 @@
return mFilters;
}
- private static byte[] generateDigest(Uri uri) {
- try {
- final MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM);
- final byte[] hostBytes = uri.getHost().getBytes();
- return digest.digest(hostBytes);
- } catch (NoSuchAlgorithmException e) {
- throw new IllegalStateException("could not find digest algorithm");
- }
- }
-
@Override
public int describeContents() {
return 0;
@@ -105,8 +87,7 @@
@Override
public void writeToParcel(Parcel out, int flags) {
- out.writeByteArray(mDigestBytes);
- out.writeInt(mDigestPrefix);
+ out.writeParcelable(mDigest, flags);
out.writeString(mPackageName);
out.writeList(mFilters);
}
@@ -136,4 +117,121 @@
return mResolveInfo;
}
}
+
+ /**
+ * Helper class to generate and store each of the digests and prefixes
+ * sent to the Ephemeral Resolver.
+ * <p>
+ * Since intent filters may want to handle multiple hosts within a
+ * domain [eg “*.google.com”], the resolver is presented with multiple
+ * hash prefixes. For example, "a.b.c.d.e" generates digests for
+ * "d.e", "c.d.e", "b.c.d.e" and "a.b.c.d.e".
+ *
+ * @hide
+ */
+ public static final class EphemeralDigest implements Parcelable {
+ /** Full digest of the domain hashes */
+ private final byte[][] mDigestBytes;
+ /** The first 4 bytes of the domain hashes */
+ private final int[] mDigestPrefix;
+
+ public EphemeralDigest(@NonNull Uri uri, int digestMask, int maxDigests) {
+ if (uri == null) {
+ throw new IllegalArgumentException();
+ }
+ mDigestBytes = generateDigest(uri, maxDigests);
+ mDigestPrefix = new int[mDigestBytes.length];
+ for (int i = 0; i < mDigestBytes.length; i++) {
+ mDigestPrefix[i] =
+ ((mDigestBytes[i][0] & 0xFF) << 24
+ | (mDigestBytes[i][1] & 0xFF) << 16
+ | (mDigestBytes[i][2] & 0xFF) << 8
+ | (mDigestBytes[i][3] & 0xFF) << 0)
+ & digestMask;
+ }
+ }
+
+ private static byte[][] generateDigest(Uri uri, int maxDigests) {
+ ArrayList<byte[]> digests = new ArrayList<>();
+ try {
+ final String host = uri.getHost().toLowerCase(Locale.ENGLISH);
+ final MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM);
+ if (maxDigests <= 0) {
+ final byte[] hostBytes = host.getBytes();
+ digests.add(digest.digest(hostBytes));
+ } else {
+ int prevDot = host.lastIndexOf('.');
+ prevDot = host.lastIndexOf('.', prevDot - 1);
+ // shortcut for short URLs
+ if (prevDot < 0) {
+ digests.add(digest.digest(host.getBytes()));
+ } else {
+ byte[] hostBytes = host.substring(prevDot + 1, host.length()).getBytes();
+ digests.add(digest.digest(hostBytes));
+ int digestCount = 1;
+ while (prevDot >= 0 && digestCount < maxDigests) {
+ prevDot = host.lastIndexOf('.', prevDot - 1);
+ hostBytes = host.substring(prevDot + 1, host.length()).getBytes();
+ digests.add(digest.digest(hostBytes));
+ digestCount++;
+ }
+ }
+ }
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException("could not find digest algorithm");
+ }
+ return digests.toArray(new byte[digests.size()][]);
+ }
+
+ EphemeralDigest(Parcel in) {
+ final int digestCount = in.readInt();
+ if (digestCount == -1) {
+ mDigestBytes = null;
+ } else {
+ mDigestBytes = new byte[digestCount][];
+ for (int i = 0; i < digestCount; i++) {
+ mDigestBytes[i] = in.createByteArray();
+ }
+ }
+ mDigestPrefix = in.createIntArray();
+ }
+
+ public byte[][] getDigestBytes() {
+ return mDigestBytes;
+ }
+
+ public int[] getDigestPrefix() {
+ return mDigestPrefix;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ if (mDigestBytes == null) {
+ out.writeInt(-1);
+ } else {
+ out.writeInt(mDigestBytes.length);
+ for (int i = 0; i < mDigestBytes.length; i++) {
+ out.writeByteArray(mDigestBytes[i]);
+ }
+ }
+ out.writeIntArray(mDigestPrefix);
+ }
+
+ @SuppressWarnings("hiding")
+ public static final Parcelable.Creator<EphemeralDigest> CREATOR =
+ new Parcelable.Creator<EphemeralDigest>() {
+ public EphemeralDigest createFromParcel(Parcel in) {
+ return new EphemeralDigest(in);
+ }
+
+ public EphemeralDigest[] newArray(int size) {
+ return new EphemeralDigest[size];
+ }
+ };
+ }
}
diff --git a/core/java/android/content/pm/IOtaDexopt.aidl b/core/java/android/content/pm/IOtaDexopt.aidl
index 8f38d6f..467bd5f 100644
--- a/core/java/android/content/pm/IOtaDexopt.aidl
+++ b/core/java/android/content/pm/IOtaDexopt.aidl
@@ -42,8 +42,21 @@
boolean isDone();
/**
+ * Return the progress (0..1) made in this session. When {@link #isDone() isDone} returns
+ * true, the progress value will be 1.
+ */
+ float getProgress();
+
+ /**
* Optimize the next package. Note: this command is synchronous, that is, only returns after
* the package has been dexopted (or dexopting failed).
+ *
+ * Note: this will be removed after a transition period. Use nextDexoptCommand instead.
*/
void dexoptNextPackage();
+
+ /**
+ * Get the optimization parameters for the next package.
+ */
+ String nextDexoptCommand();
}
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index 2ba24f6..1bf2ab0 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -28,6 +28,8 @@
ParceledListSlice getDynamicShortcuts(String packageName, int userId);
+ ParceledListSlice getManifestShortcuts(String packageName, int userId);
+
boolean addDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList,
int userId);
@@ -39,7 +41,12 @@
boolean updateShortcuts(String packageName, in ParceledListSlice shortcuts, int userId);
- int getMaxDynamicShortcutCount(String packageName, int userId);
+ void disableShortcuts(String packageName, in List shortcutIds, CharSequence disabledMessage,
+ int disabledMessageResId, int userId);
+
+ void enableShortcuts(String packageName, in List shortcutIds, int userId);
+
+ int getMaxShortcutCountPerActivity(String packageName, int userId);
int getRemainingCallCount(String packageName, int userId);
@@ -47,6 +54,8 @@
int getIconMaxDimensions(String packageName, int userId);
+ void reportShortcutUsed(String packageName, String shortcutId, int userId);
+
void resetThrottling(); // system only API for developer opsions
void onApplicationActive(String packageName, int userId); // system only API for sysUI
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 8ca27c5..6b23da9 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -20,11 +20,18 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -34,8 +41,10 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.DisplayMetrics;
import android.util.Log;
+import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -156,18 +165,19 @@
}
/**
- * Indicates that one or more shortcuts (which may be dynamic and/or pinned)
+ * Indicates that one or more shortcuts of any kind (dynamic, pinned, or manifest)
* have been added, updated or removed.
*
* <p>Only the applications that are allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}, will receive it.
*
* @param packageName The name of the package that has the shortcuts.
- * @param shortcuts all shortcuts from the package (dynamic and/or pinned). Only "key"
- * information will be provided, as defined in {@link ShortcutInfo#hasKeyFieldsOnly()}.
+ * @param shortcuts All shortcuts from the package (dynamic, manifest and/or pinned).
+ * Only "key" information will be provided, as defined in
+ * {@link ShortcutInfo#hasKeyFieldsOnly()}.
* @param user The UserHandle of the profile that generated the change.
*
- * @hide
+ * @see ShortcutManager
*/
public void onShortcutsChanged(@NonNull String packageName,
@NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
@@ -176,31 +186,68 @@
/**
* Represents a query passed to {@link #getShortcuts(ShortcutQuery, UserHandle)}.
- *
- * @hide
*/
public static class ShortcutQuery {
/**
* Include dynamic shortcuts in the result.
*/
- public static final int FLAG_GET_DYNAMIC = 1 << 0;
+ public static final int FLAG_MATCH_DYNAMIC = 1 << 0;
+
+ /** @hide kept for unit tests */
+ @Deprecated
+ public static final int FLAG_GET_DYNAMIC = FLAG_MATCH_DYNAMIC;
/**
* Include pinned shortcuts in the result.
*/
- public static final int FLAG_GET_PINNED = 1 << 1;
+ public static final int FLAG_MATCH_PINNED = 1 << 1;
+
+ /** @hide kept for unit tests */
+ @Deprecated
+ public static final int FLAG_GET_PINNED = FLAG_MATCH_PINNED;
/**
- * Requests "key" fields only. See {@link ShortcutInfo#hasKeyFieldsOnly()} for which
- * fields are available.
+ * Include manifest shortcuts in the result.
+ */
+ public static final int FLAG_MATCH_MANIFEST = 1 << 3;
+
+ /** @hide kept for unit tests */
+ @Deprecated
+ public static final int FLAG_GET_MANIFEST = FLAG_MATCH_MANIFEST;
+
+ /** @hide */
+ public static final int FLAG_MATCH_ALL_KINDS =
+ FLAG_GET_DYNAMIC | FLAG_GET_PINNED | FLAG_GET_MANIFEST;
+
+ /** @hide kept for unit tests */
+ @Deprecated
+ public static final int FLAG_GET_ALL_KINDS = FLAG_MATCH_ALL_KINDS;
+
+ /**
+ * Requests "key" fields only. See {@link ShortcutInfo#hasKeyFieldsOnly()}'s javadoc to
+ * see which fields fields "key".
+ * This allows quicker access to shortcut information in order to
+ * determine whether the caller's in-memory cache needs to be updated.
+ *
+ * <p>Typically, launcher applications cache all or most shortcut information
+ * in memory in order to show shortcuts without a delay.
+ *
+ * When a given launcher application wants to update its cache, such as when its process
+ * restarts, it can fetch shortcut information with this flag.
+ * The application can then check {@link ShortcutInfo#getLastChangedTimestamp()} for each
+ * shortcut, fetching a shortcut's non-key information only if that shortcut has been
+ * updated.
+ *
+ * @see ShortcutManager
*/
public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2;
/** @hide */
@IntDef(flag = true,
value = {
- FLAG_GET_DYNAMIC,
- FLAG_GET_PINNED,
+ FLAG_MATCH_DYNAMIC,
+ FLAG_MATCH_PINNED,
+ FLAG_MATCH_MANIFEST,
FLAG_GET_KEY_FIELDS_ONLY,
})
@Retention(RetentionPolicy.SOURCE)
@@ -224,40 +271,56 @@
}
/**
- * If non-zero, returns only shortcuts that have been added or updated since the timestamp,
- * which is a milliseconds since the Epoch.
+ * If non-zero, returns only shortcuts that have been added or updated
+ * since the given timestamp, expressed in milliseconds since the Epoch—see
+ * {@link System#currentTimeMillis()}.
*/
- public void setChangedSince(long changedSince) {
+ public ShortcutQuery setChangedSince(long changedSince) {
mChangedSince = changedSince;
+ return this;
}
/**
* If non-null, returns only shortcuts from the package.
*/
- public void setPackage(@Nullable String packageName) {
+ public ShortcutQuery setPackage(@Nullable String packageName) {
mPackage = packageName;
+ return this;
}
/**
* If non-null, return only the specified shortcuts by ID. When setting this field,
- * a packange name must also be set with {@link #setPackage}.
+ * a package name must also be set with {@link #setPackage}.
*/
- public void setShortcutIds(@Nullable List<String> shortcutIds) {
+ public ShortcutQuery setShortcutIds(@Nullable List<String> shortcutIds) {
mShortcutIds = shortcutIds;
+ return this;
}
/**
- * If non-null, returns only shortcuts associated with the activity.
+ * If non-null, returns only shortcuts associated with the activity; i.e.
+ * {@link ShortcutInfo}s whose {@link ShortcutInfo#getActivity()} are equal
+ * to {@code activity}.
*/
- public void setActivity(@Nullable ComponentName activity) {
+ public ShortcutQuery setActivity(@Nullable ComponentName activity) {
mActivity = activity;
+ return this;
}
/**
- * Set query options.
+ * Set query options. At least one of the {@code MATCH} flags should be set. Otherwise,
+ * no shortcuts will be returned.
+ *
+ * <ul>
+ * <li>{@link #FLAG_MATCH_DYNAMIC}
+ * <li>{@link #FLAG_MATCH_PINNED}
+ * <li>{@link #FLAG_MATCH_MANIFEST}
+ * <li>{@link #FLAG_GET_KEY_FIELDS_ONLY}
+ * </ul>
*/
- public void setQueryFlags(@QueryFlags int queryFlags) {
+ public ShortcutQuery setQueryFlags(@QueryFlags int queryFlags) {
mQueryFlags = queryFlags;
+ return this;
}
}
@@ -422,12 +485,16 @@
*
* <p>Only the default launcher can access the shortcut information.
*
- * <p>Note when this method returns {@code false}, that may be a temporary situation because
+ * <p>Note when this method returns {@code false}, it may be a temporary situation because
* the user is trying a new launcher application. The user may decide to change the default
- * launcher to the calling application again, so even if a launcher application loses
+ * launcher back to the calling application again, so even if a launcher application loses
* this permission, it does <b>not</b> have to purge pinned shortcut information.
+ * If the calling launcher application contains pinned shortcuts, they will still work,
+ * even though the caller no longer has the shortcut host permission.
*
- * @hide
+ * @throws IllegalStateException when the user is locked.
+ *
+ * @see ShortcutManager
*/
public boolean hasShortcutHostPermission() {
try {
@@ -438,7 +505,7 @@
}
/**
- * Returns the IDs of {@link ShortcutInfo}s that match {@code query}.
+ * Returns {@link ShortcutInfo}s that match {@code query}.
*
* <p>Callers must be allowed to access the shortcut information, as defined in {@link
* #hasShortcutHostPermission()}.
@@ -447,8 +514,10 @@
* @param user The UserHandle of the profile.
*
* @return the IDs of {@link ShortcutInfo}s that match the query.
+ * @throws IllegalStateException when the user is locked, or when the {@code user} user
+ * is locked or not running.
*
- * @hide
+ * @see ShortcutManager
*/
@Nullable
public List<ShortcutInfo> getShortcuts(@NonNull ShortcutQuery query,
@@ -467,12 +536,13 @@
* @hide // No longer used. Use getShortcuts() instead. Kept for unit tests.
*/
@Nullable
+ @Deprecated
public List<ShortcutInfo> getShortcutInfo(@NonNull String packageName,
@NonNull List<String> ids, @NonNull UserHandle user) {
final ShortcutQuery q = new ShortcutQuery();
q.setPackage(packageName);
q.setShortcutIds(ids);
- q.setQueryFlags(ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED);
+ q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS);
return getShortcuts(q, user);
}
@@ -482,14 +552,16 @@
* <p>This API is <b>NOT</b> cumulative; this will replace all pinned shortcuts for the package.
* However, different launchers may have different set of pinned shortcuts.
*
- * <p>Callers must be allowed to access the shortcut information, as defined in {@link
- * #hasShortcutHostPermission()}.
+ * <p>The calling launcher application must be allowed to access the shortcut information,
+ * as defined in {@link #hasShortcutHostPermission()}.
*
* @param packageName The target package name.
* @param shortcutIds The IDs of the shortcut to be pinned.
* @param user The UserHandle of the profile.
+ * @throws IllegalStateException when the user is locked, or when the {@code user} user
+ * is locked or not running.
*
- * @hide
+ * @see ShortcutManager
*/
public void pinShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds,
@NonNull UserHandle user) {
@@ -503,6 +575,7 @@
/**
* @hide kept for testing.
*/
+ @Deprecated
public int getShortcutIconResId(@NonNull ShortcutInfo shortcut) {
return shortcut.getIconResourceId();
}
@@ -510,46 +583,29 @@
/**
* @hide kept for testing.
*/
+ @Deprecated
public int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId,
@NonNull UserHandle user) {
final ShortcutQuery q = new ShortcutQuery();
q.setPackage(packageName);
q.setShortcutIds(Arrays.asList(shortcutId));
- q.setQueryFlags(ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED);
+ q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS);
final List<ShortcutInfo> shortcuts = getShortcuts(q, user);
return shortcuts.size() > 0 ? shortcuts.get(0).getIconResourceId() : 0;
}
/**
- * Return the icon as {@link ParcelFileDescriptor}, when it's stored as a file
- * (i.e. when {@link ShortcutInfo#hasIconFile()} returns {@code true}).
- *
- * <p>Callers must be allowed to access the shortcut information, as defined in {@link
- * #hasShortcutHostPermission()}.
- *
- * @param shortcut The target shortcut.
- *
- * @hide
+ * @hide internal/unit tests only
*/
public ParcelFileDescriptor getShortcutIconFd(
@NonNull ShortcutInfo shortcut) {
- return getShortcutIconFd(shortcut.getPackageName(), shortcut.getId(),
+ return getShortcutIconFd(shortcut.getPackage(), shortcut.getId(),
shortcut.getUserId());
}
/**
- * Return the icon as {@link ParcelFileDescriptor}, when it's stored as a file
- * (i.e. when {@link ShortcutInfo#hasIconFile()} returns {@code true}).
- *
- * <p>Callers must be allowed to access the shortcut information, as defined in {@link
- * #hasShortcutHostPermission()}.
- *
- * @param packageName The target package name.
- * @param shortcutId The ID of the shortcut to lad rom.
- * @param user The UserHandle of the profile.
- *
- * @hide
+ * @hide internal/unit tests only
*/
public ParcelFileDescriptor getShortcutIconFd(
@NonNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user) {
@@ -567,55 +623,133 @@
}
/**
- * Launches a shortcut.
+ * Returns the icon for this shortcut, without any badging for the profile.
*
- * <p>Callers must be allowed to access the shortcut information, as defined in {@link
- * #hasShortcutHostPermission()}.
+ * <p>The calling launcher application must be allowed to access the shortcut information,
+ * as defined in {@link #hasShortcutHostPermission()}.
+ *
+ * @param density The preferred density of the icon, zero for default density. Use
+ * density DPI values from {@link DisplayMetrics}.
+ *
+ * @return The drawable associated with the shortcut.
+ * @throws IllegalStateException when the user is locked, or when the {@code user} user
+ * is locked or not running.
+ *
+ * @see ShortcutManager
+ * @see #getShortcutBadgedIconDrawable(ShortcutInfo, int)
+ * @see DisplayMetrics
+ */
+ public Drawable getShortcutIconDrawable(@NonNull ShortcutInfo shortcut, int density) {
+ if (shortcut.hasIconFile()) {
+ final ParcelFileDescriptor pfd = getShortcutIconFd(shortcut);
+ if (pfd == null) {
+ return null;
+ }
+ try {
+ final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
+ return (bmp == null) ? null : new BitmapDrawable(mContext.getResources(), bmp);
+ } finally {
+ try {
+ pfd.close();
+ } catch (IOException ignore) {
+ }
+ }
+ } else if (shortcut.hasIconResource()) {
+ try {
+ final int resId = shortcut.getIconResourceId();
+ if (resId == 0) {
+ return null; // Shouldn't happen but just in case.
+ }
+ final ApplicationInfo ai = getApplicationInfo(shortcut.getPackage(),
+ /* flags =*/ 0, shortcut.getUserHandle());
+ final Resources res = mContext.getPackageManager().getResourcesForApplication(ai);
+ return res.getDrawableForDensity(resId, density);
+ } catch (NameNotFoundException | Resources.NotFoundException e) {
+ return null;
+ }
+ } else {
+ return null; // Has no icon.
+ }
+ }
+
+ /**
+ * Returns the shortcut icon with badging appropriate for the profile.
+ *
+ * <p>The calling launcher application must be allowed to access the shortcut information,
+ * as defined in {@link #hasShortcutHostPermission()}.
+ *
+ * @param density Optional density for the icon, or 0 to use the default density. Use
+ * @return A badged icon for the shortcut.
+ * @throws IllegalStateException when the user is locked, or when the {@code user} user
+ * is locked or not running.
+ *
+ * @see ShortcutManager
+ * @see #getShortcutIconDrawable(ShortcutInfo, int)
+ * @see DisplayMetrics
+ */
+ public Drawable getShortcutBadgedIconDrawable(ShortcutInfo shortcut, int density) {
+ final Drawable originalIcon = getShortcutIconDrawable(shortcut, density);
+
+ return (originalIcon == null) ? null : mContext.getPackageManager().getUserBadgedIcon(
+ originalIcon, shortcut.getUserHandle());
+ }
+
+ /**
+ * Starts a shortcut.
+ *
+ * <p>The calling launcher application must be allowed to access the shortcut information,
+ * as defined in {@link #hasShortcutHostPermission()}.
*
* @param packageName The target shortcut package name.
* @param shortcutId The target shortcut ID.
* @param sourceBounds The Rect containing the source bounds of the clicked icon.
* @param startActivityOptions Options to pass to startActivity.
* @param user The UserHandle of the profile.
- * @return {@code false} when the shortcut is no longer valid (e.g. the creator application
- * has been uninstalled). {@code true} when the shortcut is still valid.
+ * @throws IllegalStateException when the user is locked, or when the {@code user} user
+ * is locked or not running.
*
- * @hide
+ * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g.
+ * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc)
*/
- public boolean startShortcut(@NonNull String packageName, @NonNull String shortcutId,
+ public void startShortcut(@NonNull String packageName, @NonNull String shortcutId,
@Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
@NonNull UserHandle user) {
- return startShortcut(packageName, shortcutId, sourceBounds, startActivityOptions,
+ startShortcut(packageName, shortcutId, sourceBounds, startActivityOptions,
user.getIdentifier());
}
/**
* Launches a shortcut.
*
- * <p>Callers must be allowed to access the shortcut information, as defined in {@link
- * #hasShortcutHostPermission()}.
+ * <p>The calling launcher application must be allowed to access the shortcut information,
+ * as defined in {@link #hasShortcutHostPermission()}.
*
* @param shortcut The target shortcut.
* @param sourceBounds The Rect containing the source bounds of the clicked icon.
* @param startActivityOptions Options to pass to startActivity.
- * @return {@code false} when the shortcut is no longer valid (e.g. the creator application
- * has been uninstalled). {@code true} when the shortcut is still valid.
+ * @throws IllegalStateException when the user is locked, or when the {@code user} user
+ * is locked or not running.
*
- * @hide
+ * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g.
+ * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc)
*/
- public boolean startShortcut(@NonNull ShortcutInfo shortcut,
+ public void startShortcut(@NonNull ShortcutInfo shortcut,
@Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) {
- return startShortcut(shortcut.getPackageName(), shortcut.getId(),
+ startShortcut(shortcut.getPackage(), shortcut.getId(),
sourceBounds, startActivityOptions,
shortcut.getUserId());
}
- private boolean startShortcut(@NonNull String packageName, @NonNull String shortcutId,
+ private void startShortcut(@NonNull String packageName, @NonNull String shortcutId,
@Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
int userId) {
try {
- return mService.startShortcut(mContext.getPackageName(), packageName, shortcutId,
+ final boolean success =
+ mService.startShortcut(mContext.getPackageName(), packageName, shortcutId,
sourceBounds, startActivityOptions, userId);
+ if (!success) {
+ throw new ActivityNotFoundException("Shortcut could not be started");
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b06568c..281d6f6 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -132,6 +132,9 @@
MATCH_SYSTEM_ONLY,
MATCH_FACTORY_ONLY,
MATCH_DEBUG_TRIAGED_MISSING,
+ GET_DISABLED_COMPONENTS,
+ GET_DISABLED_UNTIL_USED_COMPONENTS,
+ GET_UNINSTALLED_PACKAGES,
})
@Retention(RetentionPolicy.SOURCE)
public @interface PackageInfoFlags {}
@@ -143,6 +146,9 @@
MATCH_UNINSTALLED_PACKAGES,
MATCH_SYSTEM_ONLY,
MATCH_DEBUG_TRIAGED_MISSING,
+ MATCH_DISABLED_UNTIL_USED_COMPONENTS,
+ GET_DISABLED_UNTIL_USED_COMPONENTS,
+ GET_UNINSTALLED_PACKAGES,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ApplicationInfoFlags {}
@@ -160,6 +166,9 @@
MATCH_DIRECT_BOOT_UNAWARE,
MATCH_SYSTEM_ONLY,
MATCH_UNINSTALLED_PACKAGES,
+ GET_DISABLED_COMPONENTS,
+ GET_DISABLED_UNTIL_USED_COMPONENTS,
+ GET_UNINSTALLED_PACKAGES,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ComponentInfoFlags {}
@@ -178,6 +187,9 @@
MATCH_DIRECT_BOOT_UNAWARE,
MATCH_SYSTEM_ONLY,
MATCH_UNINSTALLED_PACKAGES,
+ GET_DISABLED_COMPONENTS,
+ GET_DISABLED_UNTIL_USED_COMPONENTS,
+ GET_UNINSTALLED_PACKAGES,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ResolveInfoFlags {}
@@ -2869,6 +2881,7 @@
*
* @see #GET_META_DATA
* @see #GET_SHARED_LIBRARY_FILES
+ * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
* @see #MATCH_SYSTEM_ONLY
* @see #MATCH_UNINSTALLED_PACKAGES
*/
@@ -3497,6 +3510,7 @@
*
* @see #GET_META_DATA
* @see #GET_SHARED_LIBRARY_FILES
+ * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
* @see #MATCH_SYSTEM_ONLY
* @see #MATCH_UNINSTALLED_PACKAGES
*/
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 14f7727..d208fe7 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -157,7 +157,7 @@
int deviceOwnerUserId, String deviceOwner, SparseArray<String> profileOwners);
/**
- * Whether a package's data be cleared.
+ * Returns {@code true} if a given package can't be wiped. Otherwise, returns {@code false}.
*/
- public abstract boolean canPackageBeWiped(int userId, String packageName);
+ public abstract boolean isPackageDataProtected(int userId, String packageName);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4da77f4..2093124 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -270,6 +270,7 @@
final int nameRes;
final int labelRes;
final int iconRes;
+ final int roundIconRes;
final int logoRes;
final int bannerRes;
@@ -277,7 +278,8 @@
TypedArray sa;
ParsePackageItemArgs(Package _owner, String[] _outError,
- int _nameRes, int _labelRes, int _iconRes, int _logoRes, int _bannerRes) {
+ int _nameRes, int _labelRes, int _iconRes, int _roundIconRes, int _logoRes,
+ int _bannerRes) {
owner = _owner;
outError = _outError;
nameRes = _nameRes;
@@ -285,6 +287,7 @@
iconRes = _iconRes;
logoRes = _logoRes;
bannerRes = _bannerRes;
+ roundIconRes = _roundIconRes;
}
}
@@ -296,10 +299,12 @@
int flags;
ParseComponentArgs(Package _owner, String[] _outError,
- int _nameRes, int _labelRes, int _iconRes, int _logoRes, int _bannerRes,
+ int _nameRes, int _labelRes, int _iconRes, int _roundIconRes, int _logoRes,
+ int _bannerRes,
String[] _sepProcesses, int _processRes,
int _descriptionRes, int _enabledRes) {
- super(_owner, _outError, _nameRes, _labelRes, _iconRes, _logoRes, _bannerRes);
+ super(_owner, _outError, _nameRes, _labelRes, _iconRes, _roundIconRes, _logoRes,
+ _bannerRes);
sepProcesses = _sepProcesses;
processRes = _processRes;
descriptionRes = _descriptionRes;
@@ -2285,11 +2290,7 @@
b.append(cls);
return b.toString().intern();
}
- if (c >= 'a' && c <= 'z') {
- return cls.intern();
- }
- outError[0] = "Bad class name " + cls + " in package " + pkg;
- return null;
+ return cls.intern();
}
private static String buildCompoundName(String pkg,
@@ -2503,12 +2504,12 @@
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestPermissionGroup);
-
if (!parsePackageItemInfo(owner, perm.info, outError,
- "<permission-group>", sa,
+ "<permission-group>", sa, true /*nameRequired*/,
com.android.internal.R.styleable.AndroidManifestPermissionGroup_name,
com.android.internal.R.styleable.AndroidManifestPermissionGroup_label,
com.android.internal.R.styleable.AndroidManifestPermissionGroup_icon,
+ com.android.internal.R.styleable.AndroidManifestPermissionGroup_roundIcon,
com.android.internal.R.styleable.AndroidManifestPermissionGroup_logo,
com.android.internal.R.styleable.AndroidManifestPermissionGroup_banner)) {
sa.recycle();
@@ -2549,10 +2550,11 @@
com.android.internal.R.styleable.AndroidManifestPermission);
if (!parsePackageItemInfo(owner, perm.info, outError,
- "<permission>", sa,
+ "<permission>", sa, true /*nameRequired*/,
com.android.internal.R.styleable.AndroidManifestPermission_name,
com.android.internal.R.styleable.AndroidManifestPermission_label,
com.android.internal.R.styleable.AndroidManifestPermission_icon,
+ com.android.internal.R.styleable.AndroidManifestPermission_roundIcon,
com.android.internal.R.styleable.AndroidManifestPermission_logo,
com.android.internal.R.styleable.AndroidManifestPermission_banner)) {
sa.recycle();
@@ -2618,10 +2620,11 @@
com.android.internal.R.styleable.AndroidManifestPermissionTree);
if (!parsePackageItemInfo(owner, perm.info, outError,
- "<permission-tree>", sa,
+ "<permission-tree>", sa, true /*nameRequired*/,
com.android.internal.R.styleable.AndroidManifestPermissionTree_name,
com.android.internal.R.styleable.AndroidManifestPermissionTree_label,
com.android.internal.R.styleable.AndroidManifestPermissionTree_icon,
+ com.android.internal.R.styleable.AndroidManifestPermissionTree_roundIcon,
com.android.internal.R.styleable.AndroidManifestPermissionTree_logo,
com.android.internal.R.styleable.AndroidManifestPermissionTree_banner)) {
sa.recycle();
@@ -2668,6 +2671,7 @@
com.android.internal.R.styleable.AndroidManifestInstrumentation_name,
com.android.internal.R.styleable.AndroidManifestInstrumentation_label,
com.android.internal.R.styleable.AndroidManifestInstrumentation_icon,
+ com.android.internal.R.styleable.AndroidManifestInstrumentation_roundIcon,
com.android.internal.R.styleable.AndroidManifestInstrumentation_logo,
com.android.internal.R.styleable.AndroidManifestInstrumentation_banner);
mParseInstrumentationArgs.tag = "<instrumentation>";
@@ -2733,15 +2737,21 @@
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestApplication);
- String name = sa.getNonConfigurationString(
- com.android.internal.R.styleable.AndroidManifestApplication_name, 0);
- if (name != null) {
- ai.className = buildClassName(pkgName, name, outError);
- if (ai.className == null) {
- sa.recycle();
- mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
- return false;
- }
+ if (!parsePackageItemInfo(owner, ai, outError,
+ "<application>", sa, false /*nameRequired*/,
+ com.android.internal.R.styleable.AndroidManifestApplication_name,
+ com.android.internal.R.styleable.AndroidManifestApplication_label,
+ com.android.internal.R.styleable.AndroidManifestApplication_icon,
+ com.android.internal.R.styleable.AndroidManifestApplication_roundIcon,
+ com.android.internal.R.styleable.AndroidManifestApplication_logo,
+ com.android.internal.R.styleable.AndroidManifestApplication_banner)) {
+ sa.recycle();
+ mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+ return false;
+ }
+
+ if (ai.name != null) {
+ ai.className = ai.name;
}
String manageSpaceActivity = sa.getNonConfigurationString(
@@ -2807,18 +2817,6 @@
}
}
- TypedValue v = sa.peekValue(
- com.android.internal.R.styleable.AndroidManifestApplication_label);
- if (v != null && (ai.labelRes=v.resourceId) == 0) {
- ai.nonLocalizedLabel = v.coerceToString();
- }
-
- ai.icon = sa.getResourceId(
- com.android.internal.R.styleable.AndroidManifestApplication_icon, 0);
- ai.logo = sa.getResourceId(
- com.android.internal.R.styleable.AndroidManifestApplication_logo, 0);
- ai.banner = sa.getResourceId(
- com.android.internal.R.styleable.AndroidManifestApplication_banner, 0);
ai.theme = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_theme, 0);
ai.descriptionRes = sa.getResourceId(
@@ -3332,25 +3330,35 @@
return true;
}
- private boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo,
- String[] outError, String tag, TypedArray sa,
- int nameRes, int labelRes, int iconRes, int logoRes, int bannerRes) {
+ private static boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo,
+ String[] outError, String tag, TypedArray sa, boolean nameRequired,
+ int nameRes, int labelRes, int iconRes, int roundIconRes, int logoRes, int bannerRes) {
String name = sa.getNonConfigurationString(nameRes, 0);
if (name == null) {
- outError[0] = tag + " does not specify android:name";
- return false;
+ if (nameRequired) {
+ outError[0] = tag + " does not specify android:name";
+ return false;
+ }
+ } else {
+ outInfo.name
+ = buildClassName(owner.applicationInfo.packageName, name, outError);
+ if (outInfo.name == null) {
+ return false;
+ }
}
- outInfo.name
- = buildClassName(owner.applicationInfo.packageName, name, outError);
- if (outInfo.name == null) {
- return false;
- }
-
- int iconVal = sa.getResourceId(iconRes, 0);
- if (iconVal != 0) {
- outInfo.icon = iconVal;
+ final boolean useRoundIcon =
+ Resources.getSystem().getBoolean(com.android.internal.R.bool.config_useRoundIcon);
+ int roundIconVal = useRoundIcon ? sa.getResourceId(roundIconRes, 0) : 0;
+ if (roundIconVal != 0) {
+ outInfo.icon = roundIconVal;
outInfo.nonLocalizedLabel = null;
+ } else {
+ int iconVal = sa.getResourceId(iconRes, 0);
+ if (iconVal != 0) {
+ outInfo.icon = iconVal;
+ outInfo.nonLocalizedLabel = null;
+ }
}
int logoVal = sa.getResourceId(logoRes, 0);
@@ -3384,6 +3392,7 @@
R.styleable.AndroidManifestActivity_name,
R.styleable.AndroidManifestActivity_label,
R.styleable.AndroidManifestActivity_icon,
+ R.styleable.AndroidManifestActivity_roundIcon,
R.styleable.AndroidManifestActivity_logo,
R.styleable.AndroidManifestActivity_banner,
mSeparateProcesses,
@@ -3758,6 +3767,7 @@
com.android.internal.R.styleable.AndroidManifestActivityAlias_name,
com.android.internal.R.styleable.AndroidManifestActivityAlias_label,
com.android.internal.R.styleable.AndroidManifestActivityAlias_icon,
+ com.android.internal.R.styleable.AndroidManifestActivityAlias_roundIcon,
com.android.internal.R.styleable.AndroidManifestActivityAlias_logo,
com.android.internal.R.styleable.AndroidManifestActivityAlias_banner,
mSeparateProcesses,
@@ -3912,6 +3922,7 @@
com.android.internal.R.styleable.AndroidManifestProvider_name,
com.android.internal.R.styleable.AndroidManifestProvider_label,
com.android.internal.R.styleable.AndroidManifestProvider_icon,
+ com.android.internal.R.styleable.AndroidManifestProvider_roundIcon,
com.android.internal.R.styleable.AndroidManifestProvider_logo,
com.android.internal.R.styleable.AndroidManifestProvider_banner,
mSeparateProcesses,
@@ -4231,6 +4242,7 @@
com.android.internal.R.styleable.AndroidManifestService_name,
com.android.internal.R.styleable.AndroidManifestService_label,
com.android.internal.R.styleable.AndroidManifestService_icon,
+ com.android.internal.R.styleable.AndroidManifestService_roundIcon,
com.android.internal.R.styleable.AndroidManifestService_logo,
com.android.internal.R.styleable.AndroidManifestService_banner,
mSeparateProcesses,
@@ -4547,8 +4559,16 @@
outInfo.nonLocalizedLabel = v.coerceToString();
}
- outInfo.icon = sa.getResourceId(
- com.android.internal.R.styleable.AndroidManifestIntentFilter_icon, 0);
+ final boolean useRoundIcon =
+ Resources.getSystem().getBoolean(com.android.internal.R.bool.config_useRoundIcon);
+ int roundIconVal = useRoundIcon ? sa.getResourceId(
+ com.android.internal.R.styleable.AndroidManifestIntentFilter_roundIcon, 0) : 0;
+ if (roundIconVal != 0) {
+ outInfo.icon = roundIconVal;
+ } else {
+ outInfo.icon = sa.getResourceId(
+ com.android.internal.R.styleable.AndroidManifestIntentFilter_icon, 0);
+ }
outInfo.logo = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestIntentFilter_logo, 0);
@@ -5177,45 +5197,13 @@
public Component(final ParsePackageItemArgs args, final PackageItemInfo outInfo) {
owner = args.owner;
intents = new ArrayList<II>(0);
- String name = args.sa.getNonConfigurationString(args.nameRes, 0);
- if (name == null) {
+ if (parsePackageItemInfo(args.owner, outInfo, args.outError, args.tag, args.sa,
+ true /*nameRequired*/, args.nameRes, args.labelRes, args.iconRes,
+ args.roundIconRes, args.logoRes, args.bannerRes)) {
+ className = outInfo.name;
+ } else {
className = null;
- args.outError[0] = args.tag + " does not specify android:name";
- return;
}
-
- outInfo.name
- = buildClassName(owner.applicationInfo.packageName, name, args.outError);
- if (outInfo.name == null) {
- className = null;
- args.outError[0] = args.tag + " does not have valid android:name";
- return;
- }
-
- className = outInfo.name;
-
- int iconVal = args.sa.getResourceId(args.iconRes, 0);
- if (iconVal != 0) {
- outInfo.icon = iconVal;
- outInfo.nonLocalizedLabel = null;
- }
-
- int logoVal = args.sa.getResourceId(args.logoRes, 0);
- if (logoVal != 0) {
- outInfo.logo = logoVal;
- }
-
- int bannerVal = args.sa.getResourceId(args.bannerRes, 0);
- if (bannerVal != 0) {
- outInfo.banner = bannerVal;
- }
-
- TypedValue v = args.sa.peekValue(args.labelRes);
- if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
- outInfo.nonLocalizedLabel = v.coerceToString();
- }
-
- outInfo.packageName = owner.packageName;
}
public Component(final ParseComponentArgs args, final ComponentInfo outInfo) {
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 6162d1a..a110383 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -30,6 +30,7 @@
import android.os.UserManager;
import android.util.AtomicFile;
import android.util.AttributeSet;
+import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -54,6 +55,7 @@
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -344,6 +346,47 @@
}
}
+ public void updateServices(int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "updateServices u" + userId);
+ }
+ List<ServiceInfo<V>> allServices;
+ synchronized (mServicesLock) {
+ final UserServices<V> user = findOrCreateUserLocked(userId);
+ // If services haven't been initialized yet - no updates required
+ if (user.services == null) {
+ return;
+ }
+ allServices = new ArrayList<>(user.services.values());
+ }
+ IntArray updatedUids = null;
+ for (ServiceInfo<V> service : allServices) {
+ int versionCode = service.componentInfo.applicationInfo.versionCode;
+ String pkg = service.componentInfo.packageName;
+ ApplicationInfo newAppInfo = null;
+ try {
+ newAppInfo = mContext.getPackageManager().getApplicationInfoAsUser(pkg, 0, userId);
+ } catch (NameNotFoundException e) {
+ // Package uninstalled - treat as null app info
+ }
+ // If package updated or removed
+ if ((newAppInfo == null) || (newAppInfo.versionCode != versionCode)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Package " + pkg + " uid=" + service.uid
+ + " updated. New appInfo: " + newAppInfo);
+ }
+ if (updatedUids == null) {
+ updatedUids = new IntArray();
+ }
+ updatedUids.add(service.uid);
+ }
+ }
+ if (updatedUids != null && updatedUids.size() > 0) {
+ int[] updatedUidsArray = updatedUids.toArray();
+ generateServicesMap(updatedUidsArray, userId);
+ }
+ }
+
@VisibleForTesting
protected boolean inSystemImage(int callerUid) {
String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
@@ -379,10 +422,11 @@
*/
private void generateServicesMap(int[] changedUids, int userId) {
if (DEBUG) {
- Slog.d(TAG, "generateServicesMap() for " + userId + ", changed UIDs = " + changedUids);
+ Slog.d(TAG, "generateServicesMap() for " + userId + ", changed UIDs = "
+ + Arrays.toString(changedUids));
}
- final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<ServiceInfo<V>>();
+ final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<>();
final List<ResolveInfo> resolveInfos = queryIntentServices(userId);
for (ResolveInfo resolveInfo : resolveInfos) {
try {
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 4340d04..ed0ac53 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -19,57 +19,79 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.TaskStackBuilder;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
import java.util.Set;
-// TODO Enhance javadoc
/**
+ * Represents a "launcher shortcut" that can be published via {@link ShortcutManager}.
*
- * Represents a shortcut from an application.
- *
- * <p>Notes about icons:
- * <ul>
- * <li>If an {@link Icon} is a resource, the system keeps the package name and the resource ID.
- * Otherwise, the bitmap is fetched when it's registered to ShortcutManager,
- * then shrunk if necessary, and persisted.
- * <li>The system disallows byte[] icons, because they can easily go over the binder size limit.
- * </ul>
- *
- * @see {@link ShortcutManager}.
- *
- * @hide
+ * @see ShortcutManager
*/
public final class ShortcutInfo implements Parcelable {
- /* @hide */
+ static final String TAG = "Shortcut";
+
+ private static final String RES_TYPE_STRING = "string";
+
+ private static final String ANDROID_PACKAGE_NAME = "android";
+
+ private static final int IMPLICIT_RANK_MASK = 0x7fffffff;
+
+ private static final int RANK_CHANGED_BIT = ~IMPLICIT_RANK_MASK;
+
+ /** @hide */
+ public static final int RANK_NOT_SET = Integer.MAX_VALUE;
+
+ /** @hide */
public static final int FLAG_DYNAMIC = 1 << 0;
- /* @hide */
+ /** @hide */
public static final int FLAG_PINNED = 1 << 1;
- /* @hide */
+ /** @hide */
public static final int FLAG_HAS_ICON_RES = 1 << 2;
- /* @hide */
+ /** @hide */
public static final int FLAG_HAS_ICON_FILE = 1 << 3;
- /* @hide */
+ /** @hide */
public static final int FLAG_KEY_FIELDS_ONLY = 1 << 4;
/** @hide */
+ public static final int FLAG_MANIFEST = 1 << 5;
+
+ /** @hide */
+ public static final int FLAG_DISABLED = 1 << 6;
+
+ /** @hide */
+ public static final int FLAG_STRINGS_RESOLVED = 1 << 7;
+
+ /** @hide */
+ public static final int FLAG_IMMUTABLE = 1 << 8;
+
+ /** @hide */
@IntDef(flag = true,
value = {
FLAG_DYNAMIC,
@@ -77,26 +99,34 @@
FLAG_HAS_ICON_RES,
FLAG_HAS_ICON_FILE,
FLAG_KEY_FIELDS_ONLY,
+ FLAG_MANIFEST,
+ FLAG_DISABLED,
+ FLAG_STRINGS_RESOLVED,
+ FLAG_IMMUTABLE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ShortcutFlags {}
// Cloning options.
- /* @hide */
+ /** @hide */
private static final int CLONE_REMOVE_ICON = 1 << 0;
- /* @hide */
+ /** @hide */
private static final int CLONE_REMOVE_INTENT = 1 << 1;
- /* @hide */
+ /** @hide */
public static final int CLONE_REMOVE_NON_KEY_INFO = 1 << 2;
- /* @hide */
- public static final int CLONE_REMOVE_FOR_CREATOR = CLONE_REMOVE_ICON;
+ /** @hide */
+ public static final int CLONE_REMOVE_RES_NAMES = 1 << 3;
- /* @hide */
- public static final int CLONE_REMOVE_FOR_LAUNCHER = CLONE_REMOVE_ICON | CLONE_REMOVE_INTENT;
+ /** @hide */
+ public static final int CLONE_REMOVE_FOR_CREATOR = CLONE_REMOVE_ICON | CLONE_REMOVE_RES_NAMES;
+
+ /** @hide */
+ public static final int CLONE_REMOVE_FOR_LAUNCHER = CLONE_REMOVE_ICON | CLONE_REMOVE_INTENT
+ | CLONE_REMOVE_RES_NAMES;
/** @hide */
@IntDef(flag = true,
@@ -104,6 +134,7 @@
CLONE_REMOVE_ICON,
CLONE_REMOVE_INTENT,
CLONE_REMOVE_NON_KEY_INFO,
+ CLONE_REMOVE_RES_NAMES,
CLONE_REMOVE_FOR_CREATOR,
CLONE_REMOVE_FOR_LAUNCHER
})
@@ -111,7 +142,7 @@
public @interface CloneFlags {}
/**
- * Shortcut category for
+ * Shortcut category for messaging related actions, such as chat.
*/
public static final String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
@@ -121,33 +152,57 @@
private final String mPackageName;
@Nullable
- private ComponentName mActivityComponent;
+ private ComponentName mActivity;
@Nullable
private Icon mIcon;
- @NonNull
- private String mTitle;
+ private int mTitleResId;
+
+ private String mTitleResName;
@Nullable
- private String mText;
+ private CharSequence mTitle;
- @NonNull
+ private int mTextResId;
+
+ private String mTextResName;
+
+ @Nullable
+ private CharSequence mText;
+
+ private int mDisabledMessageResId;
+
+ private String mDisabledMessageResName;
+
+ @Nullable
+ private CharSequence mDisabledMessage;
+
+ @Nullable
private ArraySet<String> mCategories;
/**
- * Intent *with extras removed*.
+ * Intents *with extras removed*.
*/
- @NonNull
- private Intent mIntent;
+ @Nullable
+ private Intent[] mIntents;
/**
- * Extras for the intent.
+ * Extras for the intents.
*/
- @NonNull
- private PersistableBundle mIntentPersistableExtras;
+ @Nullable
+ private PersistableBundle[] mIntentPersistableExtrases;
- private int mWeight;
+ private int mRank;
+
+ /**
+ * Internally used for auto-rank-adjustment.
+ *
+ * RANK_CHANGED_BIT is used to denote that the rank of a shortcut is changing.
+ * The rest of the bits are used to denote the order in which shortcuts are passed to
+ * APIs, which is used to preserve the argument order when ranks are tie.
+ */
+ private int mImplicitRank;
@Nullable
private PersistableBundle mExtras;
@@ -159,7 +214,9 @@
private int mFlags;
// Internal use only.
- private int mIconResourceId;
+ private int mIconResId;
+
+ private String mIconResName;
// Internal use only.
@Nullable
@@ -175,26 +232,81 @@
// Note we can't do other null checks here because SM.updateShortcuts() takes partial
// information.
mPackageName = b.mContext.getPackageName();
- mActivityComponent = b.mActivityComponent;
+ mActivity = b.mActivity;
mIcon = b.mIcon;
mTitle = b.mTitle;
+ mTitleResId = b.mTitleResId;
mText = b.mText;
- mCategories = clone(b.mCategories);
- mIntent = b.mIntent;
- if (mIntent != null) {
- final Bundle intentExtras = mIntent.getExtras();
- if (intentExtras != null) {
- mIntent.replaceExtras((Bundle) null);
- mIntentPersistableExtras = new PersistableBundle(intentExtras);
- }
- }
- mWeight = b.mWeight;
+ mTextResId = b.mTextResId;
+ mDisabledMessage = b.mDisabledMessage;
+ mDisabledMessageResId = b.mDisabledMessageResId;
+ mCategories = cloneCategories(b.mCategories);
+ mIntents = cloneIntents(b.mIntents);
+ fixUpIntentExtras();
+ mRank = b.mRank;
mExtras = b.mExtras;
updateTimestamp();
}
- private <T> ArraySet<T> clone(Set<T> source) {
- return (source == null) ? null : new ArraySet<>(source);
+ /**
+ * Extract extras from {@link #mIntents} and set them to {@link #mIntentPersistableExtrases}
+ * as {@link PersistableBundle}, and remove extras from the original intents.
+ */
+ private void fixUpIntentExtras() {
+ if (mIntents == null) {
+ mIntentPersistableExtrases = null;
+ return;
+ }
+ mIntentPersistableExtrases = new PersistableBundle[mIntents.length];
+ for (int i = 0; i < mIntents.length; i++) {
+ final Intent intent = mIntents[i];
+ final Bundle extras = intent.getExtras();
+ if (extras == null) {
+ mIntentPersistableExtrases[i] = null;
+ } else {
+ mIntentPersistableExtrases[i] = new PersistableBundle(extras);
+ intent.replaceExtras((Bundle) null);
+ }
+ }
+ }
+
+ private static ArraySet<String> cloneCategories(Set<String> source) {
+ if (source == null) {
+ return null;
+ }
+ final ArraySet<String> ret = new ArraySet<>(source.size());
+ for (CharSequence s : source) {
+ if (!TextUtils.isEmpty(s)) {
+ ret.add(s.toString().intern());
+ }
+ }
+ return ret;
+ }
+
+ private static Intent[] cloneIntents(Intent[] intents) {
+ if (intents == null) {
+ return null;
+ }
+ final Intent[] ret = new Intent[intents.length];
+ for (int i = 0; i < ret.length; i++) {
+ if (intents[i] != null) {
+ ret[i] = new Intent(intents[i]);
+ }
+ }
+ return ret;
+ }
+
+ private static PersistableBundle[] clonePersistableBundle(PersistableBundle[] bundle) {
+ if (bundle == null) {
+ return null;
+ }
+ final PersistableBundle[] ret = new PersistableBundle[bundle.length];
+ for (int i = 0; i < ret.length; i++) {
+ if (bundle[i] != null) {
+ ret[i] = new PersistableBundle(bundle[i]);
+ }
+ }
+ return ret;
}
/**
@@ -204,8 +316,12 @@
*/
public void enforceMandatoryFields() {
Preconditions.checkStringNotEmpty(mId, "Shortcut ID must be provided");
- Preconditions.checkStringNotEmpty(mTitle, "Shortcut title must be provided");
- Preconditions.checkNotNull(mIntent, "Shortcut Intent must be provided");
+ Preconditions.checkNotNull(mActivity, "Activity must be provided");
+ if (mTitle == null && mTitleResId == 0) {
+ throw new IllegalArgumentException("Short label must be provided");
+ }
+ Preconditions.checkNotNull(mIntents, "Shortcut Intent must be provided");
+ Preconditions.checkArgument(mIntents.length > 0, "Shortcut Intent must be provided");
}
/**
@@ -215,14 +331,14 @@
mUserId = source.mUserId;
mId = source.mId;
mPackageName = source.mPackageName;
+ mActivity = source.mActivity;
mFlags = source.mFlags;
mLastChangedTimestamp = source.mLastChangedTimestamp;
// Just always keep it since it's cheep.
- mIconResourceId = source.mIconResourceId;
+ mIconResId = source.mIconResId;
if ((cloneFlags & CLONE_REMOVE_NON_KEY_INFO) == 0) {
- mActivityComponent = source.mActivityComponent;
if ((cloneFlags & CLONE_REMOVE_ICON) == 0) {
mIcon = source.mIcon;
@@ -230,14 +346,26 @@
}
mTitle = source.mTitle;
+ mTitleResId = source.mTitleResId;
mText = source.mText;
- mCategories = clone(source.mCategories);
+ mTextResId = source.mTextResId;
+ mDisabledMessage = source.mDisabledMessage;
+ mDisabledMessageResId = source.mDisabledMessageResId;
+ mCategories = cloneCategories(source.mCategories);
if ((cloneFlags & CLONE_REMOVE_INTENT) == 0) {
- mIntent = source.mIntent;
- mIntentPersistableExtras = source.mIntentPersistableExtras;
+ mIntents = cloneIntents(source.mIntents);
+ mIntentPersistableExtrases =
+ clonePersistableBundle(source.mIntentPersistableExtrases);
}
- mWeight = source.mWeight;
+ mRank = source.mRank;
mExtras = source.mExtras;
+
+ if ((cloneFlags & CLONE_REMOVE_RES_NAMES) == 0) {
+ mTitleResName = source.mTitleResName;
+ mTextResName = source.mTextResName;
+ mDisabledMessageResName = source.mDisabledMessageResName;
+ mIconResName = source.mIconResName;
+ }
} else {
// Set this bit.
mFlags |= FLAG_KEY_FIELDS_ONLY;
@@ -245,6 +373,221 @@
}
/**
+ * Load a string resource from the publisher app.
+ *
+ * @param resId resource ID
+ * @param defValue default value to be returned when the specified resource isn't found.
+ */
+ private CharSequence getResourceString(Resources res, int resId, CharSequence defValue) {
+ try {
+ return res.getString(resId);
+ } catch (NotFoundException e) {
+ Log.e(TAG, "Resource for ID=" + resId + " not found in package " + mPackageName);
+ return defValue;
+ }
+ }
+
+ /**
+ * Load the string resources for the text fields and set them to the actual value fields.
+ * This will set {@link #FLAG_STRINGS_RESOLVED}.
+ *
+ * @param res {@link Resources} for the publisher. Must have been loaded with
+ * {@link PackageManager#getResourcesForApplicationAsUser}.
+ *
+ * @hide
+ */
+ public void resolveResourceStrings(@NonNull Resources res) {
+ mFlags |= FLAG_STRINGS_RESOLVED;
+
+ if ((mTitleResId == 0) && (mTextResId == 0) && (mDisabledMessageResId == 0)) {
+ return; // Bail early.
+ }
+
+ if (mTitleResId != 0) {
+ mTitle = getResourceString(res, mTitleResId, mTitle);
+ }
+ if (mTextResId != 0) {
+ mText = getResourceString(res, mTextResId, mText);
+ }
+ if (mDisabledMessageResId != 0) {
+ mDisabledMessage = getResourceString(res, mDisabledMessageResId, mDisabledMessage);
+ }
+ }
+
+ /**
+ * Look up resource name for a given resource ID.
+ *
+ * @return a simple resource name (e.g. "text_1") when {@code withType} is false, or with the
+ * type (e.g. "string/text_1").
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static String lookUpResourceName(@NonNull Resources res, int resId, boolean withType,
+ @NonNull String packageName) {
+ if (resId == 0) {
+ return null;
+ }
+ try {
+ final String fullName = res.getResourceName(resId);
+
+ if (ANDROID_PACKAGE_NAME.equals(getResourcePackageName(fullName))) {
+ // If it's a framework resource, the value won't change, so just return the ID
+ // value as a string.
+ return String.valueOf(resId);
+ }
+ return withType ? getResourceTypeAndEntryName(fullName)
+ : getResourceEntryName(fullName);
+ } catch (NotFoundException e) {
+ Log.e(TAG, "Resource name for ID=" + resId + " not found in package " + packageName
+ + ". Resource IDs may change when the application is upgraded, and the system"
+ + " may not be able to find the correct resource.");
+ return null;
+ }
+ }
+
+ /**
+ * Extract the package name from a fully-donated resource name.
+ * e.g. "com.android.app1:drawable/icon1" -> "com.android.app1"
+ * @hide
+ */
+ @VisibleForTesting
+ public static String getResourcePackageName(@NonNull String fullResourceName) {
+ final int p1 = fullResourceName.indexOf(':');
+ if (p1 < 0) {
+ return null;
+ }
+ return fullResourceName.substring(0, p1);
+ }
+
+ /**
+ * Extract the type name from a fully-donated resource name.
+ * e.g. "com.android.app1:drawable/icon1" -> "drawable"
+ * @hide
+ */
+ @VisibleForTesting
+ public static String getResourceTypeName(@NonNull String fullResourceName) {
+ final int p1 = fullResourceName.indexOf(':');
+ if (p1 < 0) {
+ return null;
+ }
+ final int p2 = fullResourceName.indexOf('/', p1 + 1);
+ if (p2 < 0) {
+ return null;
+ }
+ return fullResourceName.substring(p1 + 1, p2);
+ }
+
+ /**
+ * Extract the type name + the entry name from a fully-donated resource name.
+ * e.g. "com.android.app1:drawable/icon1" -> "drawable/icon1"
+ * @hide
+ */
+ @VisibleForTesting
+ public static String getResourceTypeAndEntryName(@NonNull String fullResourceName) {
+ final int p1 = fullResourceName.indexOf(':');
+ if (p1 < 0) {
+ return null;
+ }
+ return fullResourceName.substring(p1 + 1);
+ }
+
+ /**
+ * Extract the entry name from a fully-donated resource name.
+ * e.g. "com.android.app1:drawable/icon1" -> "icon1"
+ * @hide
+ */
+ @VisibleForTesting
+ public static String getResourceEntryName(@NonNull String fullResourceName) {
+ final int p1 = fullResourceName.indexOf('/');
+ if (p1 < 0) {
+ return null;
+ }
+ return fullResourceName.substring(p1 + 1);
+ }
+
+ /**
+ * Return the resource ID for a given resource ID.
+ *
+ * Basically its' a wrapper over {@link Resources#getIdentifier(String, String, String)}, except
+ * if {@code resourceName} is an integer then it'll just return its value. (Which also the
+ * aforementioned method would do internally, but not documented, so doing here explicitly.)
+ *
+ * @param res {@link Resources} for the publisher. Must have been loaded with
+ * {@link PackageManager#getResourcesForApplicationAsUser}.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static int lookUpResourceId(@NonNull Resources res, @Nullable String resourceName,
+ @Nullable String resourceType, String packageName) {
+ if (resourceName == null) {
+ return 0;
+ }
+ try {
+ try {
+ // It the name can be parsed as an integer, just use it.
+ return Integer.parseInt(resourceName);
+ } catch (NumberFormatException ignore) {
+ }
+
+ return res.getIdentifier(resourceName, resourceType, packageName);
+ } catch (NotFoundException e) {
+ Log.e(TAG, "Resource ID for name=" + resourceName + " not found in package "
+ + packageName);
+ return 0;
+ }
+ }
+
+ /**
+ * Look up resource names from the resource IDs for the icon res and the text fields, and fill
+ * in the resource name fields.
+ *
+ * @param res {@link Resources} for the publisher. Must have been loaded with
+ * {@link PackageManager#getResourcesForApplicationAsUser}.
+ *
+ * @hide
+ */
+ public void lookupAndFillInResourceNames(@NonNull Resources res) {
+ if ((mTitleResId == 0) && (mTextResId == 0) && (mDisabledMessageResId == 0)
+ && (mIconResId == 0)) {
+ return; // Bail early.
+ }
+
+ // We don't need types for strings because their types are always "string".
+ mTitleResName = lookUpResourceName(res, mTitleResId, /*withType=*/ false, mPackageName);
+ mTextResName = lookUpResourceName(res, mTextResId, /*withType=*/ false, mPackageName);
+ mDisabledMessageResName = lookUpResourceName(res, mDisabledMessageResId,
+ /*withType=*/ false, mPackageName);
+
+ // But icons have multiple possible types, so include the type.
+ mIconResName = lookUpResourceName(res, mIconResId, /*withType=*/ true, mPackageName);
+ }
+
+ /**
+ * Look up resource IDs from the resource names for the icon res and the text fields, and fill
+ * in the resource ID fields.
+ *
+ * This is called when an app is updated.
+ *
+ * @hide
+ */
+ public void lookupAndFillInResourceIds(@NonNull Resources res) {
+ if ((mTitleResName == null) && (mTextResName == null) && (mDisabledMessageResName == null)
+ && (mIconResName == null)) {
+ return; // Bail early.
+ }
+
+ mTitleResId = lookUpResourceId(res, mTitleResName, RES_TYPE_STRING, mPackageName);
+ mTextResId = lookUpResourceId(res, mTextResName, RES_TYPE_STRING, mPackageName);
+ mDisabledMessageResId = lookUpResourceId(res, mDisabledMessageResName, RES_TYPE_STRING,
+ mPackageName);
+
+ // mIconResName already contains the type, so the third argument is not needed.
+ mIconResId = lookUpResourceId(res, mIconResName, null, mPackageName);
+ }
+
+ /**
* Copy a {@link ShortcutInfo}, optionally removing fields.
* @hide
*/
@@ -253,49 +596,85 @@
}
/**
+ * @hide
+ */
+ public void ensureUpdatableWith(ShortcutInfo source) {
+ Preconditions.checkState(mUserId == source.mUserId, "Owner User ID must match");
+ Preconditions.checkState(mId.equals(source.mId), "ID must match");
+ Preconditions.checkState(mPackageName.equals(source.mPackageName),
+ "Package name must match");
+ Preconditions.checkState(!isImmutable(), "Target ShortcutInfo is immutable");
+ }
+
+ /**
* Copy non-null/zero fields from another {@link ShortcutInfo}. Only "public" information
- * will be overwritten. The timestamp will be updated.
+ * will be overwritten. The timestamp will *not* be updated to be consistent with other
+ * setters (and also the clock is not injectable in this file).
*
* - Flags will not change
* - mBitmapPath will not change
* - Current time will be set to timestamp
*
+ * @throws IllegalStateException if source is not compatible.
+ *
* @hide
*/
public void copyNonNullFieldsFrom(ShortcutInfo source) {
- Preconditions.checkState(mUserId == source.mUserId, "Owner User ID must match");
- Preconditions.checkState(mId.equals(source.mId), "ID must match");
- Preconditions.checkState(mPackageName.equals(source.mPackageName),
- "Package name must match");
+ ensureUpdatableWith(source);
- if (source.mActivityComponent != null) {
- mActivityComponent = source.mActivityComponent;
+ if (source.mActivity != null) {
+ mActivity = source.mActivity;
}
if (source.mIcon != null) {
mIcon = source.mIcon;
+
+ mIconResId = 0;
+ mIconResName = null;
+ mBitmapPath = null;
}
if (source.mTitle != null) {
mTitle = source.mTitle;
+ mTitleResId = 0;
+ mTitleResName = null;
+ } else if (source.mTitleResId != 0) {
+ mTitle = null;
+ mTitleResId = source.mTitleResId;
+ mTitleResName = null;
}
+
if (source.mText != null) {
mText = source.mText;
+ mTextResId = 0;
+ mTextResName = null;
+ } else if (source.mTextResId != 0) {
+ mText = null;
+ mTextResId = source.mTextResId;
+ mTextResName = null;
+ }
+ if (source.mDisabledMessage != null) {
+ mDisabledMessage = source.mDisabledMessage;
+ mDisabledMessageResId = 0;
+ mDisabledMessageResName = null;
+ } else if (source.mDisabledMessageResId != 0) {
+ mDisabledMessage = null;
+ mDisabledMessageResId = source.mDisabledMessageResId;
+ mDisabledMessageResName = null;
}
if (source.mCategories != null) {
- mCategories = clone(source.mCategories);
+ mCategories = cloneCategories(source.mCategories);
}
- if (source.mIntent != null) {
- mIntent = source.mIntent;
- mIntentPersistableExtras = source.mIntentPersistableExtras;
+ if (source.mIntents != null) {
+ mIntents = cloneIntents(source.mIntents);
+ mIntentPersistableExtrases =
+ clonePersistableBundle(source.mIntentPersistableExtrases);
}
- if (source.mWeight != 0) {
- mWeight = source.mWeight;
+ if (source.mRank != RANK_NOT_SET) {
+ mRank = source.mRank;
}
if (source.mExtras != null) {
mExtras = source.mExtras;
}
-
- updateTimestamp();
}
/**
@@ -310,7 +689,6 @@
throw getInvalidIconException();
}
if (icon.hasTint()) {
- // TODO support it
throw new IllegalArgumentException("Icons with tints are not supported");
}
@@ -320,77 +698,123 @@
/** @hide */
public static IllegalArgumentException getInvalidIconException() {
return new IllegalArgumentException("Unsupported icon type:"
- +" only bitmap, resource and content URI are supported");
+ +" only the bitmap and resource types are supported");
}
/**
* Builder class for {@link ShortcutInfo} objects.
+ *
+ * @see ShortcutManager
*/
public static class Builder {
private final Context mContext;
private String mId;
- private ComponentName mActivityComponent;
+ private ComponentName mActivity;
private Icon mIcon;
- private String mTitle;
+ private int mTitleResId;
- private String mText;
+ private CharSequence mTitle;
+
+ private int mTextResId;
+
+ private CharSequence mText;
+
+ private int mDisabledMessageResId;
+
+ private CharSequence mDisabledMessage;
private Set<String> mCategories;
- private Intent mIntent;
+ private Intent[] mIntents;
- private int mWeight;
+ private int mRank = RANK_NOT_SET;
private PersistableBundle mExtras;
- /** Constructor. */
+ /**
+ * Old style constructor.
+ * @hide
+ */
+ @Deprecated
public Builder(Context context) {
mContext = context;
}
/**
- * Sets the ID of the shortcut. This is a mandatory field.
+ * Used with the old style constructor, kept for unit tests.
+ * @hide
*/
@NonNull
+ @Deprecated
public Builder setId(@NonNull String id) {
- mId = Preconditions.checkStringNotEmpty(id, "id");
+ mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty");
return this;
}
/**
- * Optionally sets the target activity. If it's not set, and if the caller application
- * has multiple launcher icons, this shortcut will be shown on all those icons.
- * If it's set, this shortcut will be only shown on this activity.
+ * Constructor.
*
- * <p>The package name of the target activity must match the package name of the shortcut
- * publisher.
- *
- * <p>This has nothing to do with the activity that this shortcut will launch. This is
- * a hint to the launcher app about which launcher icon to associate this shortcut with.
+ * @param context Client context.
+ * @param id ID of the shortcut.
*/
- @NonNull
- public Builder setActivityComponent(@NonNull ComponentName activityComponent) {
- mActivityComponent = Preconditions.checkNotNull(activityComponent, "activityComponent");
- return this;
+ public Builder(Context context, String id) {
+ mContext = context;
+ mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty");
}
/**
- * Optionally sets an icon.
+ * Sets the target activity. A shortcut will be shown along with this activity's icon
+ * on the launcher.
*
+ * When selecting a target activity, keep the following in mind:
* <ul>
- * <li>Tints set by {@link Icon#setTint} or {@link Icon#setTintList} are not supported.
- * <li>Bitmaps and resources are supported, but "content:" URIs are not supported.
+ * <li>All dynamic shortcuts must have a target activity. When a shortcut with no target
+ * activity is published using
+ * {@link ShortcutManager#addDynamicShortcuts(List)} or
+ * {@link ShortcutManager#setDynamicShortcuts(List)},
+ * the first main activity defined in the application's <code>AndroidManifest.xml</code>
+ * file is used.
+ *
+ * <li>Only "main" activities—ones that define the {@link Intent#ACTION_MAIN}
+ * and {@link Intent#CATEGORY_LAUNCHER} intent filters—can be target
+ * activities.
+ *
+ * <li>By default, the first main activity defined in the application manifest is
+ * the target activity.
+ *
+ * <li>A target activity must belong to the publisher application.
* </ul>
*
- * <p>For performance reasons, icons will <b>NOT</b> be available on instances
- * returned by {@link ShortcutManager} or {@link LauncherApps}. Launcher applications
- * can use {@link ShortcutInfo#getIconResourceId()} if {@link #hasIconResource()} is true.
- * Otherwise, if {@link #hasIconFile()} is true, use
- * {@link LauncherApps#getShortcutIconFd} to load the image.
+ * @see ShortcutInfo#getActivity()
+ */
+ @NonNull
+ public Builder setActivity(@NonNull ComponentName activity) {
+ mActivity = Preconditions.checkNotNull(activity, "activity cannot be null");
+ return this;
+ }
+
+ /**
+ * Sets an icon of a shortcut.
+ *
+ * <p>Icons are not available on {@link ShortcutInfo} instances
+ * returned by {@link ShortcutManager} or {@link LauncherApps}. The default launcher
+ * application can use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}
+ * or {@link LauncherApps#getShortcutBadgedIconDrawable(ShortcutInfo, int)} to fetch
+ * shortcut icons.
+ *
+ * <p>Tints set with {@link Icon#setTint} or {@link Icon#setTintList} are not supported
+ * and will be ignored.
+ *
+ * <p>Only icons created with {@link Icon#createWithBitmap(Bitmap)} and
+ * {@link Icon#createWithResource} are supported.
+ * Other types, such as URI-based icons, are not supported.
+ *
+ * @see LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)
+ * @see LauncherApps#getShortcutBadgedIconDrawable(ShortcutInfo, int)
*/
@NonNull
public Builder setIcon(Icon icon) {
@@ -399,26 +823,112 @@
}
/**
- * Sets the title of a shortcut. This is a mandatory field.
- *
- * <p>This field is intended for a concise description of a shortcut displayed under
- * an icon. The recommend max length is 10 characters.
+ * @hide We don't support resource strings for dynamic shortcuts for now. (But unit tests
+ * use it.)
*/
- @NonNull
- public Builder setTitle(@NonNull String title) {
- mTitle = Preconditions.checkStringNotEmpty(title, "title");
+ @Deprecated
+ public Builder setShortLabelResId(int shortLabelResId) {
+ Preconditions.checkState(mTitle == null, "shortLabel already set");
+ mTitleResId = shortLabelResId;
return this;
}
/**
- * Sets the text of a shortcut. This is an optional field.
+ * Sets the short title of a shortcut.
*
- * <p>This field is intended to be more descriptive than the shortcut title.
- * The recommend max length is 25 characters.
+ * <p>This is a mandatory field when publishing a new shortcut with
+ * {@link ShortcutManager#addDynamicShortcuts(List)} or
+ * {@link ShortcutManager#setDynamicShortcuts(List)}.
+ *
+ * <p>This field is intended to be a concise description of a shortcut.
+ *
+ * <p>The recommended maximum length is 10 characters.
+ *
+ * @see ShortcutInfo#getShortLabel()
*/
@NonNull
- public Builder setText(@NonNull String text) {
- mText = Preconditions.checkStringNotEmpty(text, "text");
+ public Builder setShortLabel(@NonNull CharSequence shortLabel) {
+ Preconditions.checkState(mTitleResId == 0, "shortLabelResId already set");
+ mTitle = Preconditions.checkStringNotEmpty(shortLabel, "shortLabel cannot be empty");
+ return this;
+ }
+
+ /**
+ * @hide We don't support resource strings for dynamic shortcuts for now. (But unit tests
+ * use it.)
+ */
+ @Deprecated
+ public Builder setLongLabelResId(int longLabelResId) {
+ Preconditions.checkState(mText == null, "longLabel already set");
+ mTextResId = longLabelResId;
+ return this;
+ }
+
+ /**
+ * Sets the text of a shortcut.
+ *
+ * <p>This field is intended to be more descriptive than the shortcut title. The launcher
+ * shows this instead of the short title when it has enough space.
+ *
+ * <p>The recommend maximum length is 25 characters.
+ *
+ * @see ShortcutInfo#getLongLabel()
+ */
+ @NonNull
+ public Builder setLongLabel(@NonNull CharSequence longLabel) {
+ Preconditions.checkState(mTextResId == 0, "longLabelResId already set");
+ mText = Preconditions.checkStringNotEmpty(longLabel, "longLabel cannot be empty");
+ return this;
+ }
+
+ /** @hide -- old signature, the internal code still uses it. */
+ @Deprecated
+ public Builder setTitle(@NonNull CharSequence value) {
+ return setShortLabel(value);
+ }
+
+ /** @hide -- old signature, the internal code still uses it. */
+ @Deprecated
+ public Builder setTitleResId(int value) {
+ return setShortLabelResId(value);
+ }
+
+ /** @hide -- old signature, the internal code still uses it. */
+ @Deprecated
+ public Builder setText(@NonNull CharSequence value) {
+ return setLongLabel(value);
+ }
+
+ /** @hide -- old signature, the internal code still uses it. */
+ @Deprecated
+ public Builder setTextResId(int value) {
+ return setLongLabelResId(value);
+ }
+
+ /**
+ * @hide We don't support resource strings for dynamic shortcuts for now. (But unit tests
+ * use it.)
+ */
+ @Deprecated
+ public Builder setDisabledMessageResId(int disabledMessageResId) {
+ Preconditions.checkState(mDisabledMessage == null, "disabledMessage already set");
+ mDisabledMessageResId = disabledMessageResId;
+ return this;
+ }
+
+ /**
+ * Sets the message that should be shown when the user attempts to start a shortcut that
+ * is disabled.
+ *
+ * @see ShortcutInfo#getDisabledMessage()
+ */
+ @NonNull
+ public Builder setDisabledMessage(@NonNull CharSequence disabledMessage) {
+ Preconditions.checkState(
+ mDisabledMessageResId == 0, "disabledMessageResId already set");
+ mDisabledMessage =
+ Preconditions.checkStringNotEmpty(disabledMessage,
+ "disabledMessage cannot be empty");
return this;
}
@@ -427,6 +937,7 @@
* categorise shortcuts.
*
* @see #SHORTCUT_CATEGORY_CONVERSATION
+ * @see ShortcutInfo#getCategories()
*/
@NonNull
public Builder setCategories(Set<String> categories) {
@@ -435,28 +946,70 @@
}
/**
- * Sets the intent of a shortcut. This is a mandatory field. The extras must only contain
- * persistable information. (See {@link PersistableBundle}).
+ * Sets the intent of a shortcut. Alternatively, {@link #setIntents(Intent[])} can be used
+ * to launch an activity with other activities in the back stack.
+ *
+ * <p>This is a mandatory field when publishing a new shortcut with
+ * {@link ShortcutManager#addDynamicShortcuts(List)} or
+ * {@link ShortcutManager#setDynamicShortcuts(List)}.
+ *
+ * <p>A shortcut can launch any intent that the publisher application has permission to
+ * launch. For example, a shortcut can launch an unexported activity within the publisher
+ * application. A shortcut intent doesn't have to point at the target activity.
+ *
+ * <p>The given {@code intent} can contain extras, but these extras must contain values
+ * of primitive types in order for the system to persist these values.
+ *
+ * @see ShortcutInfo#getIntent()
+ * @see #setIntents(Intent[])
*/
@NonNull
public Builder setIntent(@NonNull Intent intent) {
- mIntent = Preconditions.checkNotNull(intent, "intent");
- return this;
+ return setIntents(new Intent[]{intent});
}
/**
- * Optionally sets the weight of a shortcut, which will be used by the launcher for sorting.
- * The larger the weight, the more "important" a shortcut is.
+ * Sets multiple intents instead of a single intent, in order to launch an activity with
+ * other activities in back stack. Use {@link TaskStackBuilder} to build intents.
+ * See the {@link ShortcutManager} javadoc for details.
+ *
+ * @see Builder#setIntent(Intent)
+ * @see ShortcutInfo#getIntents()
+ * @see Context#startActivities(Intent[])
+ * @see TaskStackBuilder
*/
@NonNull
- public Builder setWeight(int weight) {
- mWeight = weight;
+ public Builder setIntents(@NonNull Intent[] intents) {
+ Preconditions.checkNotNull(intents, "intents cannot be null");
+ Preconditions.checkNotNull(intents.length, "intents cannot be empty");
+ for (Intent intent : intents) {
+ Preconditions.checkNotNull(intent, "intents cannot contain null");
+ Preconditions.checkNotNull(intent.getAction(), "intent's action must be set");
+ }
+ // Make sure always clone incoming intents.
+ mIntents = cloneIntents(intents);
return this;
}
/**
- * Optional values that applications can set. Applications can store any meta-data of
- * shortcuts in this, and retrieve later from {@link ShortcutInfo#getExtras()}.
+ * "Rank" of a shortcut, which is a non-negative value that's used by the launcher app
+ * to sort shortcuts.
+ *
+ * See {@link ShortcutInfo#getRank()} for details.
+ */
+ @NonNull
+ public Builder setRank(int rank) {
+ Preconditions.checkArgument((0 <= rank),
+ "Rank cannot be negative or bigger than MAX_RANK");
+ mRank = rank;
+ return this;
+ }
+
+ /**
+ * Extras that application can set for any purpose.
+ *
+ * <p>Applications can store arbitrary shortcut metadata in extras and retrieve the
+ * metadata later using {@link ShortcutInfo#getExtras()}.
*/
@NonNull
public Builder setExtras(@NonNull PersistableBundle extras) {
@@ -474,7 +1027,11 @@
}
/**
- * Return the ID of the shortcut.
+ * Returns the ID of a shortcut.
+ *
+ * <p>Shortcut IDs are unique within each publisher application and must be stable across
+ * devices so that shortcuts will still be valid when restored on a different device.
+ * See {@link ShortcutManager} for details.
*/
@NonNull
public String getId() {
@@ -482,33 +1039,34 @@
}
/**
- * Return the package name of the creator application.
+ * Return the package name of the publisher application.
*/
@NonNull
- public String getPackageName() {
+ public String getPackage() {
return mPackageName;
}
/**
- * Return the target activity, which may be null, in which case the shortcut is not associated
- * with a specific activity.
+ * Return the target activity.
*
- * <p>This has nothing to do with the activity that this shortcut will launch. This is
- * a hint to the launcher app that on which launcher icon this shortcut should be shown.
+ * <p>This has nothing to do with the activity that this shortcut will launch.
+ * Launcher applications should show the launcher icon for the returned activity alongside
+ * this shortcut.
*
- * @see Builder#setActivityComponent
+ * @see Builder#setActivity
*/
@Nullable
- public ComponentName getActivityComponent() {
- return mActivityComponent;
+ public ComponentName getActivity() {
+ return mActivity;
+ }
+
+ /** @hide */
+ public void setActivity(ComponentName activity) {
+ mActivity = activity;
}
/**
- * Icon.
- *
- * For performance reasons, this will <b>NOT</b> be available when an instance is returned
- * by {@link ShortcutManager} or {@link LauncherApps}. A launcher application needs to use
- * other APIs in LauncherApps to fetch the bitmap.
+ * Returns the shortcut icon.
*
* @hide
*/
@@ -517,27 +1075,82 @@
return mIcon;
}
- /**
- * Return the shortcut title.
- *
- * <p>All shortcuts must have a non-empty title, but this method will return null when
- * {@link #hasKeyFieldsOnly()} is true.
- */
+ /** @hide -- old signature, the internal code still uses it. */
@Nullable
- public String getTitle() {
+ @Deprecated
+ public CharSequence getTitle() {
return mTitle;
}
- /**
- * Return the shortcut text.
- */
+ /** @hide -- old signature, the internal code still uses it. */
+ @Deprecated
+ public int getTitleResId() {
+ return mTitleResId;
+ }
+
+ /** @hide -- old signature, the internal code still uses it. */
@Nullable
- public String getText() {
+ @Deprecated
+ public CharSequence getText() {
return mText;
}
+ /** @hide -- old signature, the internal code still uses it. */
+ @Deprecated
+ public int getTextResId() {
+ return mTextResId;
+ }
+
/**
- * Return the categories.
+ * Return the shorter description of a shortcut.
+ *
+ * @see Builder#setShortLabel(CharSequence)
+ */
+ @Nullable
+ public CharSequence getShortLabel() {
+ return mTitle;
+ }
+
+ /** @hide */
+ public int getShortLabelResourceId() {
+ return mTitleResId;
+ }
+
+ /**
+ * Return the longer description of a shortcut.
+ *
+ * @see Builder#setLongLabel(CharSequence)
+ */
+ @Nullable
+ public CharSequence getLongLabel() {
+ return mText;
+ }
+
+ /** @hide */
+ public int getLongLabelResourceId() {
+ return mTextResId;
+ }
+
+ /**
+ * Return the message that should be shown when the user attempts to start a shortcut
+ * that is disabled.
+ *
+ * @see Builder#setDisabledMessage(CharSequence)
+ */
+ @Nullable
+ public CharSequence getDisabledMessage() {
+ return mDisabledMessage;
+ }
+
+ /** @hide */
+ public int getDisabledMessageResourceId() {
+ return mDisabledMessageResId;
+ }
+
+ /**
+ * Return the shortcut's categories.
+ *
+ * @see Builder#setCategories(Set)
*/
@Nullable
public Set<String> getCategories() {
@@ -545,55 +1158,125 @@
}
/**
- * Return the intent.
+ * Returns the intent that is executed when the user selects this shortcut.
+ * If setIntents() was used, then return the last intent in the array.
*
- * <p>All shortcuts must have an intent, but this method will return null when
- * {@link #hasKeyFieldsOnly()} is true.
+ * <p>Launcher applications <b>cannot</b> see the intent. If a {@link ShortcutInfo} is
+ * obtained via {@link LauncherApps}, then this method will always return null.
+ * Launchers can only start a shortcut intent with {@link LauncherApps#startShortcut}.
*
- * <p>Launcher apps <b>cannot</b> see the intent. If a {@link ShortcutInfo} is obtained via
- * {@link LauncherApps}, then this method will always return null. Launcher apps can only
- * start a shortcut intent with {@link LauncherApps#startShortcut}.
+ * @see Builder#setIntent(Intent)
*/
@Nullable
public Intent getIntent() {
- if (mIntent == null) {
+ if (mIntents == null || mIntents.length == 0) {
return null;
}
- final Intent intent = new Intent(mIntent);
- intent.replaceExtras(
- mIntentPersistableExtras != null ? new Bundle(mIntentPersistableExtras) : null);
- return intent;
+ final int last = mIntents.length - 1;
+ final Intent intent = new Intent(mIntents[last]);
+ return setIntentExtras(intent, mIntentPersistableExtrases[last]);
}
/**
- * Return "raw" intent, which is the original intent without the extras.
+ * Return the intent set with {@link Builder#setIntents(Intent[])}.
+ *
+ * <p>Launcher applications <b>cannot</b> see the intents. If a {@link ShortcutInfo} is
+ * obtained via {@link LauncherApps}, then this method will always return null.
+ * Launchers can only start a shortcut intent with {@link LauncherApps#startShortcut}.
+ *
+ * @see Builder#setIntents(Intent[])
+ */
+ @Nullable
+ public Intent[] getIntents() {
+ final Intent[] ret = new Intent[mIntents.length];
+
+ for (int i = 0; i < ret.length; i++) {
+ ret[i] = new Intent(mIntents[i]);
+ setIntentExtras(ret[i], mIntentPersistableExtrases[i]);
+ }
+
+ return ret;
+ }
+
+ /**
+ * Return "raw" intents, which is the original intents without the extras.
* @hide
*/
@Nullable
- public Intent getIntentNoExtras() {
- return mIntent;
+ public Intent[] getIntentsNoExtras() {
+ return mIntents;
}
/**
- * The extras in the intent. We convert extras into {@link PersistableBundle} so we can
+ * The extras in the intents. We convert extras into {@link PersistableBundle} so we can
* persist them.
* @hide
*/
@Nullable
- public PersistableBundle getIntentPersistableExtras() {
- return mIntentPersistableExtras;
+ public PersistableBundle[] getIntentPersistableExtrases() {
+ return mIntentPersistableExtrases;
}
/**
- * Return the weight of a shortcut, which will be used by Launcher for sorting.
- * The larger the weight, the more "important" a shortcut is.
+ * "Rank" of a shortcut, which is a non-negative, sequential value that's unique for each
+ * {@link #getActivity} for each of the two kinds, dynamic shortcuts and manifest shortcuts.
+ *
+ * <p>Because manifest shortcuts and dynamic shortcuts have overlapping ranks,
+ * when a launcher application shows shortcuts for an activity, it should first show
+ * the manifest shortcuts followed by the dynamic shortcuts. Within each of those categories,
+ * shortcuts should be sorted by rank in ascending order.
+ *
+ * <p>"Floating" shortcuts (i.e. shortcuts that are neither dynamic nor manifest) will all
+ * have rank 0, because there's no sorting for them.
+ *
+ * See the {@link ShortcutManager}'s class javadoc for details.
+ *
+ * @see Builder#setRank(int)
*/
- public int getWeight() {
- return mWeight;
+ public int getRank() {
+ return mRank;
+ }
+
+ /** @hide */
+ public boolean hasRank() {
+ return mRank != RANK_NOT_SET;
+ }
+
+ /** @hide */
+ public void setRank(int rank) {
+ mRank = rank;
+ }
+
+ /** @hide */
+ public void clearImplicitRankAndRankChangedFlag() {
+ mImplicitRank = 0;
+ }
+
+ /** @hide */
+ public void setImplicitRank(int rank) {
+ // Make sure to keep RANK_CHANGED_BIT.
+ mImplicitRank = (mImplicitRank & RANK_CHANGED_BIT) | (rank & IMPLICIT_RANK_MASK);
+ }
+
+ /** @hide */
+ public int getImplicitRank() {
+ return mImplicitRank & IMPLICIT_RANK_MASK;
+ }
+
+ /** @hide */
+ public void setRankChanged() {
+ mImplicitRank |= RANK_CHANGED_BIT;
+ }
+
+ /** @hide */
+ public boolean isRankChanged() {
+ return (mImplicitRank & RANK_CHANGED_BIT) != 0;
}
/**
- * Optional values that application can set.
+ * Extras that application can set to any purposes.
+ *
+ * @see Builder#setExtras(PersistableBundle)
*/
@Nullable
public PersistableBundle getExtras() {
@@ -606,7 +1289,7 @@
}
/**
- * {@link UserHandle} on which the publisher created shortcuts.
+ * {@link UserHandle} on which the publisher created this shortcut.
*/
public UserHandle getUserHandle() {
return UserHandle.of(mUserId);
@@ -656,18 +1339,93 @@
}
/**
+ * Return whether a shortcut is published from AndroidManifest.xml or not. If {@code true},
+ * it's also {@link #isImmutable()}.
+ *
+ * <p>When an app is upgraded and a shortcut is no longer published from AndroidManifest.xml,
+ * this will be set to {@code false}. If the shortcut is not pinned, then it'll just disappear.
+ * However, if it's pinned, it will still be alive, and {@link #isEnabled()} will be
+ * {@code false} and {@link #isImmutable()} will be {@code true}.
+ */
+ public boolean isDeclaredInManifest() {
+ return hasFlags(FLAG_MANIFEST);
+ }
+
+ /** @hide kept for unit tests */
+ @Deprecated
+ public boolean isManifestShortcut() {
+ return isDeclaredInManifest();
+ }
+
+ /**
+ * @return true if pinned but neither dynamic nor manifest.
+ * @hide
+ */
+ public boolean isFloating() {
+ return isPinned() && !(isDynamic() || isManifestShortcut());
+ }
+
+ /** @hide */
+ public boolean isOriginallyFromManifest() {
+ return hasFlags(FLAG_IMMUTABLE);
+ }
+
+ /**
+ * Return if a shortcut is immutable, in which case it cannot be modified with any of
+ * {@link ShortcutManager} APIs.
+ *
+ * <p>All manifest shortcuts are immutable. When a manifest shortcut is pinned and then
+ * disabled because the app is upgraded and its AndroidManifest.xml no longer publishes it,
+ * {@link #isDeclaredInManifest()} returns {@code false}, but it is still immutable.
+ *
+ * <p>All shortcuts originally published via the {@link ShortcutManager} APIs
+ * are all mutable.
+ */
+ public boolean isImmutable() {
+ return hasFlags(FLAG_IMMUTABLE);
+ }
+
+ /**
+ * Returns {@code false} if a shortcut is disabled with
+ * {@link ShortcutManager#disableShortcuts}.
+ */
+ public boolean isEnabled() {
+ return !hasFlags(FLAG_DISABLED);
+ }
+
+ /** @hide */
+ public boolean isAlive() {
+ return hasFlags(FLAG_PINNED) || hasFlags(FLAG_DYNAMIC) || hasFlags(FLAG_MANIFEST);
+ }
+
+ /** @hide */
+ public boolean usesQuota() {
+ return hasFlags(FLAG_DYNAMIC) || hasFlags(FLAG_MANIFEST);
+ }
+
+ /**
* Return whether a shortcut's icon is a resource in the owning package.
*
- * @see LauncherApps#getShortcutIconResId(ShortcutInfo)
+ * @hide internal/unit tests only
*/
public boolean hasIconResource() {
return hasFlags(FLAG_HAS_ICON_RES);
}
+ /** @hide */
+ public boolean hasStringResources() {
+ return (mTitleResId != 0) || (mTextResId != 0) || (mDisabledMessageResId != 0);
+ }
+
+ /** @hide */
+ public boolean hasAnyResources() {
+ return hasIconResource() || hasStringResources();
+ }
+
/**
* Return whether a shortcut's icon is stored as a file.
*
- * @see LauncherApps#getShortcutIconFd(ShortcutInfo)
+ * @hide internal/unit tests only
*/
public boolean hasIconFile() {
return hasFlags(FLAG_HAS_ICON_FILE);
@@ -678,19 +1436,33 @@
* following fields are available.
* <ul>
* <li>{@link #getId()}
- * <li>{@link #getPackageName()}
+ * <li>{@link #getPackage()}
+ * <li>{@link #getActivity()}
* <li>{@link #getLastChangedTimestamp()}
* <li>{@link #isDynamic()}
* <li>{@link #isPinned()}
- * <li>{@link #hasIconResource()}
- * <li>{@link #hasIconFile()}
+ * <li>{@link #isDeclaredInManifest()}
+ * <li>{@link #isImmutable()}
+ * <li>{@link #isEnabled()}
+ * <li>{@link #getUserHandle()}
* </ul>
+ *
+ * <p>For performance reasons, shortcuts passed to
+ * {@link LauncherApps.Callback#onShortcutsChanged(String, List, UserHandle)} as well as those
+ * returned from {@link LauncherApps#getShortcuts(ShortcutQuery, UserHandle)}
+ * while using the {@link ShortcutQuery#FLAG_GET_KEY_FIELDS_ONLY} option contain only key
+ * information.
*/
public boolean hasKeyFieldsOnly() {
return hasFlags(FLAG_KEY_FIELDS_ONLY);
}
/** @hide */
+ public boolean hasStringResourcesResolved() {
+ return hasFlags(FLAG_STRINGS_RESOLVED);
+ }
+
+ /** @hide */
public void updateTimestamp() {
mLastChangedTimestamp = System.currentTimeMillis();
}
@@ -708,14 +1480,18 @@
/** @hide */
public void setIconResourceId(int iconResourceId) {
- mIconResourceId = iconResourceId;
+ if (mIconResId != iconResourceId) {
+ mIconResName = null;
+ }
+ mIconResId = iconResourceId;
}
/**
* Get the resource ID for the icon, valid only when {@link #hasIconResource()} } is true.
+ * @hide internal / tests only.
*/
public int getIconResourceId() {
- return mIconResourceId;
+ return mIconResId;
}
/** @hide */
@@ -728,25 +1504,129 @@
mBitmapPath = bitmapPath;
}
+ /** @hide */
+ public void setDisabledMessageResId(int disabledMessageResId) {
+ if (mDisabledMessageResId != disabledMessageResId) {
+ mDisabledMessageResName = null;
+ }
+ mDisabledMessageResId = disabledMessageResId;
+ mDisabledMessage = null;
+ }
+
+ /** @hide */
+ public void setDisabledMessage(String disabledMessage) {
+ mDisabledMessage = disabledMessage;
+ mDisabledMessageResId = 0;
+ mDisabledMessageResName = null;
+ }
+
+ /** @hide */
+ public String getTitleResName() {
+ return mTitleResName;
+ }
+
+ /** @hide */
+ public void setTitleResName(String titleResName) {
+ mTitleResName = titleResName;
+ }
+
+ /** @hide */
+ public String getTextResName() {
+ return mTextResName;
+ }
+
+ /** @hide */
+ public void setTextResName(String textResName) {
+ mTextResName = textResName;
+ }
+
+ /** @hide */
+ public String getDisabledMessageResName() {
+ return mDisabledMessageResName;
+ }
+
+ /** @hide */
+ public void setDisabledMessageResName(String disabledMessageResName) {
+ mDisabledMessageResName = disabledMessageResName;
+ }
+
+ /** @hide */
+ public String getIconResName() {
+ return mIconResName;
+ }
+
+ /** @hide */
+ public void setIconResName(String iconResName) {
+ mIconResName = iconResName;
+ }
+
+ /**
+ * Replaces the intent
+ *
+ * @throws IllegalArgumentException when extra is not compatible with {@link PersistableBundle}.
+ *
+ * @hide
+ */
+ public void setIntents(Intent[] intents) throws IllegalArgumentException {
+ Preconditions.checkNotNull(intents);
+ Preconditions.checkArgument(intents.length > 0);
+
+ mIntents = cloneIntents(intents);
+ fixUpIntentExtras();
+ }
+
+ /** @hide */
+ public static Intent setIntentExtras(Intent intent, PersistableBundle extras) {
+ if (extras == null) {
+ intent.replaceExtras((Bundle) null);
+ } else {
+ intent.replaceExtras(new Bundle(extras));
+ }
+ return intent;
+ }
+
+ /**
+ * Replaces the categories.
+ *
+ * @hide
+ */
+ public void setCategories(Set<String> categories) {
+ mCategories = cloneCategories(categories);
+ }
+
private ShortcutInfo(Parcel source) {
final ClassLoader cl = getClass().getClassLoader();
mUserId = source.readInt();
mId = source.readString();
mPackageName = source.readString();
- mActivityComponent = source.readParcelable(cl);
- mIcon = source.readParcelable(cl);
- mTitle = source.readString();
- mText = source.readString();
- mIntent = source.readParcelable(cl);
- mIntentPersistableExtras = source.readParcelable(cl);
- mWeight = source.readInt();
- mExtras = source.readParcelable(cl);
- mLastChangedTimestamp = source.readLong();
+ mActivity = source.readParcelable(cl);
mFlags = source.readInt();
- mIconResourceId = source.readInt();
+ mIconResId = source.readInt();
+ mLastChangedTimestamp = source.readLong();
+
+ if (source.readInt() == 0) {
+ return; // key information only.
+ }
+
+ mIcon = source.readParcelable(cl);
+ mTitle = source.readCharSequence();
+ mTitleResId = source.readInt();
+ mText = source.readCharSequence();
+ mTextResId = source.readInt();
+ mDisabledMessage = source.readCharSequence();
+ mDisabledMessageResId = source.readInt();
+ mIntents = source.readParcelableArray(cl, Intent.class);
+ mIntentPersistableExtrases = source.readParcelableArray(cl, PersistableBundle.class);
+ mRank = source.readInt();
+ mExtras = source.readParcelable(cl);
mBitmapPath = source.readString();
+ mIconResName = source.readString();
+ mTitleResName = source.readString();
+ mTextResName = source.readString();
+ mDisabledMessageResName = source.readString();
+
int N = source.readInt();
if (N == 0) {
mCategories = null;
@@ -763,20 +1643,36 @@
dest.writeInt(mUserId);
dest.writeString(mId);
dest.writeString(mPackageName);
- dest.writeParcelable(mActivityComponent, flags);
- dest.writeParcelable(mIcon, flags);
- dest.writeString(mTitle);
- dest.writeString(mText);
-
- dest.writeParcelable(mIntent, flags);
- dest.writeParcelable(mIntentPersistableExtras, flags);
- dest.writeInt(mWeight);
- dest.writeParcelable(mExtras, flags);
- dest.writeLong(mLastChangedTimestamp);
+ dest.writeParcelable(mActivity, flags);
dest.writeInt(mFlags);
- dest.writeInt(mIconResourceId);
+ dest.writeInt(mIconResId);
+ dest.writeLong(mLastChangedTimestamp);
+
+ if (hasKeyFieldsOnly()) {
+ dest.writeInt(0);
+ return;
+ }
+ dest.writeInt(1);
+
+ dest.writeParcelable(mIcon, flags);
+ dest.writeCharSequence(mTitle);
+ dest.writeInt(mTitleResId);
+ dest.writeCharSequence(mText);
+ dest.writeInt(mTextResId);
+ dest.writeCharSequence(mDisabledMessage);
+ dest.writeInt(mDisabledMessageResId);
+
+ dest.writeParcelableArray(mIntents, flags);
+ dest.writeParcelableArray(mIntentPersistableExtrases, flags);
+ dest.writeInt(mRank);
+ dest.writeParcelable(mExtras, flags);
dest.writeString(mBitmapPath);
+ dest.writeString(mIconResName);
+ dest.writeString(mTitleResName);
+ dest.writeString(mTextResName);
+ dest.writeString(mDisabledMessageResName);
+
if (mCategories != null) {
final int N = mCategories.size();
dest.writeInt(N);
@@ -823,24 +1719,67 @@
sb.append("id=");
sb.append(secure ? "***" : mId);
+ sb.append(", flags=0x");
+ sb.append(Integer.toHexString(mFlags));
+ sb.append(" [");
+ if (!isEnabled()) {
+ sb.append("X");
+ }
+ if (isImmutable()) {
+ sb.append("Im");
+ }
+ if (isManifestShortcut()) {
+ sb.append("M");
+ }
+ if (isDynamic()) {
+ sb.append("D");
+ }
+ if (isPinned()) {
+ sb.append("P");
+ }
+ if (hasIconFile()) {
+ sb.append("If");
+ }
+ if (hasIconResource()) {
+ sb.append("Ir");
+ }
+ if (hasKeyFieldsOnly()) {
+ sb.append("K");
+ }
+ if (hasStringResourcesResolved()) {
+ sb.append("Sr");
+ }
+ sb.append("]");
+
sb.append(", packageName=");
sb.append(mPackageName);
- if (isDynamic()) {
- sb.append(", dynamic");
- }
- if (isPinned()) {
- sb.append(", pinned");
- }
-
sb.append(", activity=");
- sb.append(mActivityComponent);
+ sb.append(mActivity);
- sb.append(", title=");
+ sb.append(", shortLabel=");
sb.append(secure ? "***" : mTitle);
+ sb.append(", resId=");
+ sb.append(mTitleResId);
+ sb.append("[");
+ sb.append(mTitleResName);
+ sb.append("]");
- sb.append(", text=");
+ sb.append(", longLabel=");
sb.append(secure ? "***" : mText);
+ sb.append(", resId=");
+ sb.append(mTextResId);
+ sb.append("[");
+ sb.append(mTextResName);
+ sb.append("]");
+
+ sb.append(", disabledMessage=");
+ sb.append(secure ? "***" : mDisabledMessage);
+ sb.append(", resId=");
+ sb.append(mDisabledMessageResId);
+ sb.append("[");
+ sb.append(mDisabledMessageResName);
+ sb.append("]");
sb.append(", categories=");
sb.append(mCategories);
@@ -848,28 +1787,44 @@
sb.append(", icon=");
sb.append(mIcon);
- sb.append(", weight=");
- sb.append(mWeight);
+ sb.append(", rank=");
+ sb.append(mRank);
sb.append(", timestamp=");
sb.append(mLastChangedTimestamp);
- sb.append(", intent=");
- sb.append(mIntent);
-
- sb.append(", intentExtras=");
- sb.append(secure ? "***" : mIntentPersistableExtras);
+ sb.append(", intents=");
+ if (mIntents == null) {
+ sb.append("null");
+ } else {
+ if (secure) {
+ sb.append("size:");
+ sb.append(mIntents.length);
+ } else {
+ final int size = mIntents.length;
+ sb.append("[");
+ String sep = "";
+ for (int i = 0; i < size; i++) {
+ sb.append(sep);
+ sep = ", ";
+ sb.append(mIntents[i]);
+ sb.append("/");
+ sb.append(mIntentPersistableExtrases[i]);
+ }
+ sb.append("]");
+ }
+ }
sb.append(", extras=");
sb.append(mExtras);
- sb.append(", flags=");
- sb.append(mFlags);
-
if (includeInternalData) {
sb.append(", iconRes=");
- sb.append(mIconResourceId);
+ sb.append(mIconResId);
+ sb.append("[");
+ sb.append(mIconResName);
+ sb.append("]");
sb.append(", bitmapPath=");
sb.append(mBitmapPath);
@@ -881,26 +1836,36 @@
/** @hide */
public ShortcutInfo(
- @UserIdInt int userId, String id, String packageName, ComponentName activityComponent,
- Icon icon, String title, String text, Set<String> categories, Intent intent,
- PersistableBundle intentPersistableExtras,
- int weight, PersistableBundle extras, long lastChangedTimestamp,
- int flags, int iconResId, String bitmapPath) {
+ @UserIdInt int userId, String id, String packageName, ComponentName activity,
+ Icon icon, CharSequence title, int titleResId, String titleResName,
+ CharSequence text, int textResId, String textResName,
+ CharSequence disabledMessage, int disabledMessageResId, String disabledMessageResName,
+ Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras,
+ long lastChangedTimestamp,
+ int flags, int iconResId, String iconResName, String bitmapPath) {
mUserId = userId;
mId = id;
mPackageName = packageName;
- mActivityComponent = activityComponent;
+ mActivity = activity;
mIcon = icon;
mTitle = title;
+ mTitleResId = titleResId;
+ mTitleResName = titleResName;
mText = text;
- mCategories = clone(categories);
- mIntent = intent;
- mIntentPersistableExtras = intentPersistableExtras;
- mWeight = weight;
+ mTextResId = textResId;
+ mTextResName = textResName;
+ mDisabledMessage = disabledMessage;
+ mDisabledMessageResId = disabledMessageResId;
+ mDisabledMessageResName = disabledMessageResName;
+ mCategories = cloneCategories(categories);
+ mIntents = cloneIntents(intentsWithExtras);
+ fixUpIntentExtras();
+ mRank = rank;
mExtras = extras;
mLastChangedTimestamp = lastChangedTimestamp;
mFlags = flags;
- mIconResourceId = iconResId;
+ mIconResId = iconResId;
+ mIconResName = iconResName;
mBitmapPath = bitmapPath;
}
}
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 16486e1..cd248ea 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -17,8 +17,11 @@
import android.annotation.NonNull;
import android.annotation.TestApi;
+import android.annotation.UserIdInt;
+import android.app.Activity;
+import android.app.usage.UsageStatsManager;
import android.content.Context;
-import android.os.IBinder;
+import android.content.Intent;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -27,69 +30,416 @@
import java.util.List;
-// TODO Enhance javadoc
/**
- * {@link ShortcutManager} manages shortcuts created by applications.
+ * The ShortcutManager manages "launcher shortcuts" (or simply "shortcuts"). Shortcuts provide
+ * users
+ * with quick access to activities other than an application's main activity in the currently-active
+ * launcher. For example,
+ * an email application may publish the "compose new email" action, which will directly open the
+ * compose activity. The {@link ShortcutInfo} class contains information about each of the
+ * shortcuts themselves.
*
- * <h3>Dynamic shortcuts and pinned shortcuts</h3>
+ * <h3>Dynamic Shortcuts and Manifest Shortcuts</h3>
*
- * An application can publish shortcuts with {@link #setDynamicShortcuts(List)} and
- * {@link #addDynamicShortcuts(List)}. There can be at most
- * {@link #getMaxDynamicShortcutCount()} number of dynamic shortcuts at a time from the same
+ * There are two ways to publish shortcuts: manifest shortcuts and dynamic shortcuts.
+ *
+ * <ul>
+ * <li>Manifest shortcuts are declared in a resource
+ * XML, which is referenced in the publisher application's <code>AndroidManifest.xml</code> file.
+ * Manifest shortcuts are published when an application is installed,
+ * and the details of these shortcuts change when an application is upgraded with an updated XML
+ * file.
+ * Manifest shortcuts are immutable, and their
+ * definitions, such as icons and labels, cannot be changed dynamically without upgrading the
+ * publisher application.
+ *
+ * <li>Dynamic shortcuts are published at runtime using the {@link ShortcutManager} APIs.
+ * Applications can publish, update, and remove dynamic shortcuts at runtime.
+ * </ul>
+ *
+ * <p>Only "main" activities—activities that handle the {@code MAIN} action and the
+ * {@code LAUNCHER} category—can have shortcuts.
+ * If an application has multiple main activities, these activities will have different sets
+ * of shortcuts.
+ *
+ * <p>Dynamic shortcuts and manifest shortcuts are shown in the currently active launcher when
+ * the user long-presses on an application launcher icon. The actual gesture may be different
+ * depending on the launcher application.
+ *
+ * <p>Each launcher icon can have at most {@link #getMaxShortcutCountPerActivity()} number of
+ * dynamic and manifest shortcuts combined.
+ *
+ *
+ * <h3>Pinning Shortcuts</h3>
+ *
+ * Launcher applications allow users to "pin" shortcuts so they're easier to access. Both manifest
+ * and dynamic shortcuts can be pinned.
+ * Pinned shortcuts <b>cannot</b> be removed by publisher
+ * applications; they're removed only when the user removes them,
+ * when the publisher application is uninstalled, or when the
+ * user performs the "clear data" action on the publisher application from the device's Settings
* application.
- * A dynamic shortcut can be deleted with {@link #removeDynamicShortcuts(List)}, and apps
- * can also use {@link #removeAllDynamicShortcuts()} to delete all dynamic shortcuts.
*
- * <p>The shortcuts that are currently published by the above APIs are called "dynamic", because
- * they can be removed by the creator application at any time. The user may "pin" dynamic shortcuts
- * on Launcher to make "pinned" shortcuts. Pinned shortcuts <b>cannot</b> be removed by the creator
- * app. An application can obtain all pinned shortcuts from itself with
- * {@link #getPinnedShortcuts()}. Applications should keep the pinned shortcut information
- * up-to-date using {@link #updateShortcuts(List)}.
+ * <p>However, the publisher application can <em>disable</em> pinned shortcuts so they cannot be
+ * started. See the following sections for details.
*
- * <p>The number of pinned shortcuts does not affect the number of dynamic shortcuts that can be
- * published by an application at a time.
- * No matter how many pinned shortcuts that Launcher has for an application, the
- * application can still always publish {@link #getMaxDynamicShortcutCount()} number of dynamic
- * shortcuts.
*
- * <h3>Shortcut IDs</h3>
+ * <h3>Updating and Disabling Shortcuts</h3>
*
- * Each shortcut must have an ID, which must be unique within each application. When a shortcut is
- * published, existing shortcuts with the same ID will be updated. Note this may include a
- * pinned shortcut.
+ * <p>When a dynamic shortcut is pinned, even when the publisher removes it as a dynamic shortcut,
+ * the pinned shortcut will still be visible and launchable. This allows an application to have
+ * more than {@link #getMaxShortcutCountPerActivity()} number of shortcuts.
*
- * <h3>Rate limiting</h3>
+ * <p>For example, suppose {@link #getMaxShortcutCountPerActivity()} is 5:
+ * <ul>
+ * <li>A chat application publishes 5 dynamic shortcuts for the 5 most recent
+ * conversations, "c1" - "c5".
*
- * Calls to {@link #setDynamicShortcuts(List)}, {@link #addDynamicShortcuts(List)},
- * and {@link #updateShortcuts(List)} from <b>background applications</b> will be
- * rate-limited. An application can call these methods at most
- * {@link #getRemainingCallCount()} times until the rate-limiting counter is reset,
- * which happens at a certain time every day.
+ * <li>The user pins all 5 of the shortcuts.
*
- * <p>An application can use {@link #getRateLimitResetTime()} to get the next reset time.
+ * <li>Later, the user has started 3 additional conversations ("c6", "c7", and "c8"),
+ * so the publisher application
+ * re-publishes its dynamic shortcuts. The new dynamic shortcut list is:
+ * "c4", "c5", "c6", "c7", and "c8".
+ * The publisher application has to remove "c1", "c2", and "c3" because it can't have more than
+ * 5 dynamic shortcuts.
*
- * <p>Foreground applications (i.e. ones with a foreground activity or a foreground services)
- * will not be throttled. Also, when an application comes to foreground,
- * {@link #getRemainingCallCount()} will be reset to the initial value.
+ * <li>However, even though "c1", "c2" and "c3" are no longer dynamic shortcuts, the pinned
+ * shortcuts for these conversations are still available and launchable.
*
- * <p>For testing purposes, use "Developer Options" (found in the Settings menu) to reset the
- * internal rate-limiting counter. Automated tests can use the following ADB shell command to
- * achieve the same effect:</p>
- * <pre>adb shell cmd shortcut reset-throttling</pre>
+ * <li>At this point, the user can access a total of 8 shortcuts that link to activities in
+ * the publisher application, including the 3 pinned
+ * shortcuts, even though it's allowed to have at most 5 dynamic shortcuts.
+ *
+ * <li>The application can use {@link #updateShortcuts(List)} to update any of the existing
+ * 8 shortcuts, when, for example, the chat peers' icons have changed.
+ * </ul>
+ * The {@link #addDynamicShortcuts(List)} and {@link #setDynamicShortcuts(List)} methods
+ * can also be used
+ * to update existing shortcuts with the same IDs, but they <b>cannot</b> be used
+ * for updating non-dynamic, pinned shortcuts because these two methods try to convert the given
+ * lists of shortcuts to dynamic shortcuts.
+ *
+ *
+ * <h4>Disabling Manifest Shortcuts</h4>
+ * When an application is upgraded and the new version
+ * no longer uses a manifest shortcut that appeared in the previous version, this deprecated
+ * shortcut will no longer be published as a manifest shortcut.
+ *
+ * <p>If the deprecated shortcut is pinned, then the pinned shortcut will remain on the launcher,
+ * but it will be disabled automatically.
+ * Note that, in this case, the pinned shortcut is no longer a manifest shortcut, but it's
+ * still <b>immutable</b> and cannot be updated using the {@link ShortcutManager} APIs.
+ *
+ *
+ * <h4>Disabling Dynamic Shortcuts</h4>
+ * Sometimes pinned shortcuts become obsolete and may not be usable. For example, a pinned shortcut
+ * to a group chat will be unusable when the associated group chat is deleted. In cases like this,
+ * applications should use {@link #disableShortcuts(List)}, which will remove the specified dynamic
+ * shortcuts and also make any specified pinned shortcuts un-launchable.
+ * The {@link #disableShortcuts(List, CharSequence)} method can also be used to disabled shortcuts
+ * and show users a custom error message when they attempt to launch the disabled shortcuts.
+ *
+ *
+ * <h3>Publishing Manifest Shortcuts</h3>
+ *
+ * In order to add manifest shortcuts to your application, first add
+ * {@code <meta-data android:name="android.app.shortcuts" />} to your main activity in
+ * AndroidManifest.xml:
+ * <pre>
+ * <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ * package="com.example.myapplication">
+ * <application . . .>
+ * <activity android:name="Main">
+ * <intent-filter>
+ * <action android:name="android.intent.action.MAIN" />
+ * <category android:name="android.intent.category.LAUNCHER" />
+ * </intent-filter>
+ * <b><meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/></b>
+ * </activity>
+ * </application>
+ * </manifest>
+ * </pre>
+ *
+ * Then, define your application's manifest shortcuts in the <code>res/xml/shortcuts.xml</code>
+ * file:
+ * <pre>
+ * <shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ * <shortcut
+ * android:shortcutId="compose"
+ * android:enabled="true"
+ * android:icon="@drawable/compose_icon"
+ * android:shortcutShortLabel="@string/compose_shortcut_short_label1"
+ * android:shortcutLongLabel="@string/compose_shortcut_long_label1"
+ * android:shortcutDisabledMessage="@string/compose_disabled_message1"
+ * >
+ * <intent
+ * android:action="android.intent.action.VIEW"
+ * android:targetPackage="com.example.myapplication"
+ * android:targetClass="com.example.myapplication.ComposeActivity" />
+ * <!-- more intents can go here; see below -->
+ * <categories android:name="android.shortcut.conversation" />
+ * </shortcut>
+ * <!-- more shortcuts can go here -->
+ * </shortcuts>
+ * </pre>
+ *
+ * The following list includes descriptions for the different attributes within a manifest shortcut:
+ * <dl>
+ * <dt>android:shortcutId</dt>
+ * <dd>Mandatory shortcut ID</dd>
+ *
+ * <dt>android:enabled</dt>
+ * <dd>Default is {@code true}. Can be set to {@code false} in order
+ * to disable a manifest shortcut that was published in a previous version and and set a custom
+ * disabled message. If a custom disabled message is not needed, then a manifest shortcut can
+ * be simply removed from the XML file rather than keeping it with {@code enabled="false"}.</dd>
+ *
+ * <dt>android:icon</dt>
+ * <dd>Shortcut icon.</dd>
+ *
+ * <dt>android:shortcutShortLabel</dt>
+ * <dd>Mandatory shortcut short label.
+ * See {@link ShortcutInfo.Builder#setShortLabel(CharSequence)}.</dd>
+ *
+ * <dt>android:shortcutLongLabel</dt>
+ * <dd>Shortcut long label.
+ * See {@link ShortcutInfo.Builder#setLongLabel(CharSequence)}.</dd>
+ *
+ * <dt>android:shortcutDisabledMessage</dt>
+ * <dd>When {@code android:enabled} is set to
+ * {@code false}, this attribute is used to display a custom disabled message.</dd>
+ *
+ * <dt>intent</dt>
+ * <dd>Intent to launch when the user selects the shortcut.
+ * {@code android:action} is mandatory.
+ * See <a href="{@docRoot}guide/topics/ui/settings.html#Intents">Using intents</a> for the
+ * other supported tags.
+ * You can provide multiple intents for a single shortcut so that an activity is launched
+ * with other activities in the back stack. See {@link android.app.TaskStackBuilder} for details.
+ * </dd>
+ * <dt>categories</dt>
+ * <dd>Specify shortcut categories. Currently only
+ * {@link ShortcutInfo#SHORTCUT_CATEGORY_CONVERSATION} is defined in the framework.
+ * </dd>
+ * </dl>
+ *
+ * <h3>Publishing Dynamic Shortcuts</h3>
+ *
+ * Applications can publish dynamic shortcuts with {@link #setDynamicShortcuts(List)}
+ * or {@link #addDynamicShortcuts(List)}. The {@link #updateShortcuts(List)} method can also be
+ * used to update existing, mutable shortcuts.
+ * Use {@link #removeDynamicShortcuts(List)} or {@link #removeAllDynamicShortcuts()} to remove
+ * dynamic shortcuts.
+ *
+ * <p>Example:
+ * <pre>
+ * ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
+ *
+ * ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "id1")
+ * .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.mysite.com/")))
+ * .setShortLabel("Web site")
+ * .setLongLabel("Open the web site")
+ * .setIcon(Icon.createWithResource(context, R.drawable.icon_website))
+ * .build();
+ *
+ * shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut));
+ * </pre>
+ *
+ *
+ * <h3>Shortcut Intents</h3>
+ * Dynamic shortcuts can be published with any set of {@link Intent#addFlags Intent} flags.
+ * Typically, {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} is specified, possibly along with other
+ * flags; otherwise, if the application is already running, the application is simply brought to
+ * the foreground, and the target activity may not appear.
+ *
+ * <p>The {@link ShortcutInfo.Builder#setIntents(Intent[])} method can be used instead of
+ * {@link ShortcutInfo.Builder#setIntent(Intent)} with {@link android.app.TaskStackBuilder}
+ * in order to launch an activity with other activities in the back stack.
+ * When the user selects a shortcut to load an activity with a back stack,
+ * then presses the back key, a "parent" activity will be shown instead of the user being
+ * navigated back to the launcher.
+ *
+ * <p>Manifest shortcuts can also have multiple intents to achieve the same effect.
+ * In order to associate multiple {@link Intent} objects with a shortcut, simply list multiple
+ * <code><intent></code> elements within a single <code><shortcut></code> element.
+ * The last intent specifies what the user will see when they launch a shortcut.
+ *
+ * <p>Manifest shortcuts <b>cannot</b> have custom intent flags.
+ * The first intent of a manifest shortcut will always have {@link Intent#FLAG_ACTIVITY_NEW_TASK}
+ * and {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} set.
+ * This means, when the application is already running, all the existing activities will be
+ * destroyed when a manifest shortcut is launched.
+ * If this behavior is not desirable, you can use a <em>trampoline activity</em>,
+ * or an invisible activity that starts another activity in {@link Activity#onCreate},
+ * then calls {@link Activity#finish()}.
+ * The first activity should include an attribute setting
+ * of {@code android:taskAffinity=""} in the application's <code>AndroidManifest.xml</code>
+ * file, and the intent within the manifest shortcut should point at this first activity.
+ *
+ *
+ * <h3>Showing New Information in a Shortcut</h3>
+ * In order to avoid confusion, you should not use {@link #updateShortcuts(List)} to update
+ * a shortcut so that it contains conceptually different information.
+ *
+ * <p>For example, a phone application may publish the most frequently called contact as a dynamic
+ * shortcut. Over time, this contact may change; when it does, the application should
+ * represent the changed contact with a new shortcut that contains a different ID, using either
+ * {@link #setDynamicShortcuts(List)} or {@link #addDynamicShortcuts(List)}, rather than updating
+ * the existing shortcut with {@link #updateShortcuts(List)}.
+ * This is because when the shortcut is pinned, changing
+ * it to reference a different contact will likely confuse the user.
+ *
+ * <p>On the other hand, when the
+ * contact's information has changed, such as the name or picture, the application should
+ * use {@link #updateShortcuts(List)} so that the pinned shortcut is updated too.
+ *
+ *
+ * <h3>Shortcut Display Order</h3>
+ * When the launcher displays the shortcuts that are associated with a particular launcher icon,
+ * the shortcuts should appear in the following order:
+ * <ul>
+ * <li>First show manifest shortcuts
+ * (if {@link ShortcutInfo#isDeclaredInManifest()} is {@code true}),
+ * and then show dynamic shortcuts (if {@link ShortcutInfo#isDynamic()} is {@code true}).
+ * <li>Within each category of shortcuts (manifest and dynamic), sort the shortcuts in order
+ * of increasing rank according to {@link ShortcutInfo#getRank()}.
+ * </ul>
+ * <p>Shortcut ranks are non-negative sequential integers
+ * that determine the order in which shortcuts appear, assuming that the shortcuts are all in
+ * the same category.
+ * Ranks of existing shortcuts can be updated with
+ * {@link #updateShortcuts(List)}; you can use {@link #addDynamicShortcuts(List)} and
+ * {@link #setDynamicShortcuts(List)}, too.
+ *
+ * <p>Ranks are auto-adjusted so that they're unique for each target activity in each category
+ * (dynamic or manifest). For example, if there are 3 dynamic shortcuts with ranks 0, 1 and 2,
+ * adding another dynamic shortcut with a rank of 1 represents a request to place this shortcut at
+ * the second position.
+ * In response, the third and fourth shortcuts move closer to the bottom of the shortcut list,
+ * with their ranks changing to 2 and 3, respectively.
+ *
+ * <h3>Rate Limiting</h3>
+ *
+ * Calls to {@link #setDynamicShortcuts(List)}, {@link #addDynamicShortcuts(List)}, and
+ * {@link #updateShortcuts(List)} may be rate-limited when called by background applications, or
+ * applications with no foreground activity or service. When you attempt to call these methods
+ * from a background application after exceeding the rate limit, these APIs return {@code false}.
+ *
+ * <p>Applications with a foreground activity or service are not rate-limited.
+ *
+ * <p>Rate-limiting will be reset upon certain events, so that even background applications
+ * can call these APIs again until they are rate limit is reached again.
+ * These events include the following:
+ * <ul>
+ * <li>When an application comes to the foreground.
+ * <li>When the system locale changes.
+ * <li>When the user performs an "inline reply" action on a notification.
+ * </ul>
+ *
+ * <p>When rate-limiting is active, {@link #isRateLimitingActive()} returns {@code true}.
+ *
+ * <h4>Resetting rate-limiting for testing</h4>
+ *
+ * If your application is rate-limited during development or testing, you can use the
+ * "Reset ShortcutManager rate-limiting" development option or the following adb command to reset
+ * it:
+ * <pre>
+ * adb shell cmd shortcut reset-throttling [ --user USER-ID ]
+ * </pre>
+ *
+ * <h3>Handling System Locale Changes</h3>
+ *
+ * Applications should update dynamic and pinned shortcuts when the system locale changes
+ * using the {@link Intent#ACTION_LOCALE_CHANGED} broadcast.
+ *
+ * <p>When the system locale changes, rate-limiting is reset, so even background applications
+ * can set dynamic shortcuts, add dynamic shortcuts, and update shortcuts until the rate limit
+ * is reached again.
+ *
*
* <h3>Backup and Restore</h3>
*
- * Shortcuts will be backed up and restored across devices. This means all information, including
- * IDs, must be meaningful on a different device.
+ * When an application has the {@code android:allowBackup="true"} attribute assignment included
+ * in its <code>AndroidManifest.xml</code> file, pinned shortcuts are
+ * backed up automatically and are restored when the user sets up a new device.
*
- * <h3>APIs for launcher</h3>
+ * <h4>Categories of Shortcuts that are Backed Up</h4>
*
- * Launcher applications should use {@link LauncherApps} to get shortcuts that are published from
- * applications. Launcher applications can also pin shortcuts with
- * {@link LauncherApps#pinShortcuts(String, List, UserHandle)}.
+ * <ul>
+ * <li>Pinned shortcuts are backed up. Bitmap icons are not backed up by the system,
+ * but launcher applications should back them up and restore them so that the user still sees icons
+ * for pinned shortcuts on the launcher. Applications can always use
+ * {@link #updateShortcuts(List)} to re-publish icons.
*
- * @hide
+ * <li>Manifest shortcuts are not backed up, but when an application is re-installed on a new
+ * device, they are re-published from the <code>AndroidManifest.xml</code> file, anyway.
+ *
+ * <li>Dynamic shortcuts are <b>not</b> backed up.
+ * </ul>
+ *
+ * <p>Because dynamic shortcuts are not restored, it is recommended that applications check
+ * currently-published dynamic shortcuts using {@link #getDynamicShortcuts()}
+ * each time they are launched, and they should re-publish
+ * dynamic shortcuts when necessary.
+ *
+ * <pre>
+ * public class MainActivity extends Activity {
+ * public void onCreate(Bundle savedInstanceState) {
+ * super.onCreate(savedInstanceState);
+ *
+ * ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
+ *
+ * if (shortcutManager.getDynamicShortcuts().size() == 0) {
+ * // Application restored; re-publish dynamic shortcuts.
+ *
+ * if (shortcutManager.getPinnedShortcuts().size() > 0) {
+ * // Pinned shortcuts have been restored. Use updateShortcuts() to make sure
+ * // they have up-to-date information.
+ * }
+ * }
+ * }
+ * :
+ *
+ * }
+ * </pre>
+ *
+ *
+ * <h4>Backup/restore and shortcut IDs</h4>
+ *
+ * Because pinned shortcuts are backed up and restored on new devices, shortcut IDs should be
+ * meaningful across devices; that is, IDs should contain either stable, constant strings
+ * or server-side identifiers,
+ * rather than identifiers generated locally that might not make sense on other devices.
+ *
+ *
+ * <h3>Report Shortcut Usage and Prediction</h3>
+ *
+ * Launcher applications may be capable of predicting which shortcuts will most likely be
+ * used at a given time by examining the shortcut usage history data.
+ *
+ * <p>In order to provide launchers with such data, publisher applications should
+ * report the shortcuts that are used with {@link #reportShortcutUsed(String)}
+ * when a shortcut is selected,
+ * <b>or when an action equivalent to a shortcut is taken by the user even if it wasn't started
+ * with the shortcut</b>.
+ *
+ * <p>For example, suppose a GPS navigation application supports "navigate to work" as a shortcut.
+ * It should then report when the user selects this shortcut <b>and</b> when the user chooses
+ * to navigate to work within the application itself.
+ * This helps the launcher application
+ * learn that the user wants to navigate to work at a certain time every
+ * weekday, and it can then show this shortcut in a suggestion list at the right time.
+ *
+ * <h3>Launcher API</h3>
+ *
+ * The {@link LauncherApps} class provides APIs for launcher applications to access shortcuts.
+ *
+ *
+ * <h3>Direct Boot and Shortcuts</h3>
+ *
+ * All shortcut information is stored in credential encrypted storage, so no shortcuts can be
+ * accessed when the user is locked.
*/
public class ShortcutManager {
private static final String TAG = "ShortcutManager";
@@ -115,15 +465,18 @@
}
/**
- * Publish a list of shortcuts. All existing dynamic shortcuts from the caller application
- * will be replaced.
+ * Publish the list of shortcuts. All existing dynamic shortcuts from the caller application
+ * will be replaced. If there are already pinned shortcuts with the same IDs,
+ * the mutable pinned shortcuts are updated.
*
* <p>This API will be rate-limited.
*
* @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited.
*
- * @throws IllegalArgumentException if {@code shortcutInfoList} contains more than
- * {@link #getMaxDynamicShortcutCount()} shortcuts.
+ * @throws IllegalArgumentException if {@link #getMaxShortcutCountPerActivity()} is exceeded,
+ * or when trying to update immutable shortcuts.
+ *
+ * @throws IllegalStateException when the user is locked.
*/
public boolean setDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
try {
@@ -135,8 +488,9 @@
}
/**
- * Return all dynamic shortcuts from the caller application. The number of result items
- * will not exceed the value returned by {@link #getMaxDynamicShortcutCount()}.
+ * Return all dynamic shortcuts from the caller application.
+ *
+ * @throws IllegalStateException when the user is locked.
*/
@NonNull
public List<ShortcutInfo> getDynamicShortcuts() {
@@ -149,15 +503,32 @@
}
/**
- * Publish a single dynamic shortcut. If there's already dynamic or pinned shortcuts with
- * the same ID, they will all be updated.
+ * Return all manifest shortcuts from the caller application.
+ *
+ * @throws IllegalStateException when the user is locked.
+ */
+ @NonNull
+ public List<ShortcutInfo> getManifestShortcuts() {
+ try {
+ return mService.getManifestShortcuts(mContext.getPackageName(), injectMyUserId())
+ .getList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Publish the list of dynamic shortcuts. If there are already dynamic or pinned shortcuts with
+ * the same IDs, each mutable shortcut is updated.
*
* <p>This API will be rate-limited.
*
* @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited.
*
- * @throws IllegalArgumentException if the caller application has already published the
- * max number of dynamic shortcuts.
+ * @throws IllegalArgumentException if {@link #getMaxShortcutCountPerActivity()} is exceeded,
+ * or when trying to update immutable shortcuts.
+ *
+ * @throws IllegalStateException when the user is locked.
*/
public boolean addDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
try {
@@ -169,7 +540,9 @@
}
/**
- * Delete a single dynamic shortcut by ID.
+ * Delete dynamic shortcuts by ID.
+ *
+ * @throws IllegalStateException when the user is locked.
*/
public void removeDynamicShortcuts(@NonNull List<String> shortcutIds) {
try {
@@ -182,6 +555,8 @@
/**
* Delete all dynamic shortcuts from the caller application.
+ *
+ * @throws IllegalStateException when the user is locked.
*/
public void removeAllDynamicShortcuts() {
try {
@@ -193,6 +568,8 @@
/**
* Return all pinned shortcuts from the caller application.
+ *
+ * @throws IllegalStateException when the user is locked.
*/
@NonNull
public List<ShortcutInfo> getPinnedShortcuts() {
@@ -205,11 +582,16 @@
}
/**
- * Update all existing shortcuts with the same IDs. Shortcuts may be pinned and/or dynamic.
+ * Update all existing shortcuts with the same IDs. Target shortcuts may be pinned and/or
+ * dynamic, but they must not be immutable.
*
* <p>This API will be rate-limited.
*
* @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited.
+ *
+ * @throws IllegalArgumentException If trying to update immutable shortcuts.
+ *
+ * @throws IllegalStateException when the user is locked.
*/
public boolean updateShortcuts(List<ShortcutInfo> shortcutInfoList) {
try {
@@ -221,11 +603,94 @@
}
/**
- * Return the max number of dynamic shortcuts that each application can have at a time.
+ * Disable pinned shortcuts. For more details, see the Javadoc for the {@link ShortcutManager}
+ * class.
+ *
+ * @throws IllegalArgumentException If trying to disable immutable shortcuts.
+ *
+ * @throws IllegalStateException when the user is locked.
*/
- public int getMaxDynamicShortcutCount() {
+ public void disableShortcuts(@NonNull List<String> shortcutIds) {
try {
- return mService.getMaxDynamicShortcutCount(mContext.getPackageName(), injectMyUserId());
+ mService.disableShortcuts(mContext.getPackageName(), shortcutIds,
+ /* disabledMessage =*/ null, /* disabledMessageResId =*/ 0,
+ injectMyUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide old signature, kept for unit testing.
+ */
+ public void disableShortcuts(@NonNull List<String> shortcutIds, int disabledMessageResId) {
+ try {
+ mService.disableShortcuts(mContext.getPackageName(), shortcutIds,
+ /* disabledMessage =*/ null, disabledMessageResId,
+ injectMyUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide old signature, kept for unit testing.
+ */
+ public void disableShortcuts(@NonNull List<String> shortcutIds, String disabledMessage) {
+ disableShortcuts(shortcutIds, (CharSequence) disabledMessage);
+ }
+
+ /**
+ * Disable pinned shortcuts, showing the user a custom error message when they try to select
+ * the disabled shortcuts.
+ * For more details, see the Javadoc for the {@link ShortcutManager} class.
+ *
+ * @throws IllegalArgumentException If trying to disable immutable shortcuts.
+ *
+ * @throws IllegalStateException when the user is locked.
+ */
+ public void disableShortcuts(@NonNull List<String> shortcutIds, CharSequence disabledMessage) {
+ try {
+ mService.disableShortcuts(mContext.getPackageName(), shortcutIds,
+ disabledMessage, /* disabledMessageResId =*/ 0,
+ injectMyUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Re-enable pinned shortcuts that were previously disabled. If the target shortcuts
+ * already enabled, this method does nothing.
+ *
+ * @throws IllegalArgumentException If trying to enable immutable shortcuts.
+ *
+ * @throws IllegalStateException when the user is locked.
+ */
+ public void enableShortcuts(@NonNull List<String> shortcutIds) {
+ try {
+ mService.enableShortcuts(mContext.getPackageName(), shortcutIds, injectMyUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
+ * @hide old signature, kept for unit testing.
+ */
+ public int getMaxShortcutCountForActivity() {
+ return getMaxShortcutCountPerActivity();
+ }
+
+ /**
+ * Return the maximum number of dynamic and manifest shortcuts that each launcher icon
+ * can have at a time.
+ */
+ public int getMaxShortcutCountPerActivity() {
+ try {
+ return mService.getMaxShortcutCountPerActivity(
+ mContext.getPackageName(), injectMyUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -236,6 +701,8 @@
* before the rate limit counter is reset.
*
* @see #getRateLimitResetTime()
+ *
+ * @hide
*/
public int getRemainingCallCount() {
try {
@@ -250,6 +717,8 @@
*
* @see #getRemainingCallCount()
* @see System#currentTimeMillis()
+ *
+ * @hide
*/
public long getRateLimitResetTime() {
try {
@@ -260,16 +729,82 @@
}
/**
- * Return the max width and height for icons, in pixels.
+ * Return {@code true} when rate-limiting is active for the caller application.
+ *
+ * <p>See the class level javadoc for details.
+ *
+ * @throws IllegalStateException when the user is locked.
*/
- public int getIconMaxDimensions() {
+ public boolean isRateLimitingActive() {
try {
+ return mService.getRemainingCallCount(mContext.getPackageName(), injectMyUserId())
+ == 0;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return the max width for icons, in pixels.
+ */
+ public int getIconMaxWidth() {
+ try {
+ // TODO Implement it properly using xdpi.
return mService.getIconMaxDimensions(mContext.getPackageName(), injectMyUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ /**
+ * Return the max height for icons, in pixels.
+ */
+ public int getIconMaxHeight() {
+ try {
+ // TODO Implement it properly using ydpi.
+ return mService.getIconMaxDimensions(mContext.getPackageName(), injectMyUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Applications that publish shortcuts should call this method
+ * whenever the user selects the shortcut containing the given ID or when the user completes
+ * an action in the application that is equivalent to selecting the shortcut.
+ * For more details, see the Javadoc for the {@link ShortcutManager} class
+ *
+ * <p>The information is accessible via {@link UsageStatsManager#queryEvents}
+ * Typically, launcher applications use this information to build a prediction model
+ * so that they can promote the shortcuts that are likely to be used at the moment.
+ *
+ * @throws IllegalStateException when the user is locked.
+ */
+ public void reportShortcutUsed(String shortcutId) {
+ try {
+ mService.reportShortcutUsed(mContext.getPackageName(), shortcutId,
+ injectMyUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Called internally when an application is considered to have come to foreground
+ * even when technically it's not. This method resets the throttling for this package.
+ * For example, when the user sends an "inline reply" on an notification, the system UI will
+ * call it.
+ *
+ * @hide
+ */
+ public void onApplicationActive(@NonNull String packageName, @UserIdInt int userId) {
+ try {
+ mService.onApplicationActive(packageName, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** @hide injection point */
@VisibleForTesting
protected int injectMyUserId() {
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index 3f8bad1..af56105 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -53,7 +53,8 @@
@NonNull String callingPackage, @NonNull String packageName,
@NonNull List<String> shortcutIds, int userId);
- public abstract Intent createShortcutIntent(int launcherUserId, @NonNull String callingPackage,
+ public abstract Intent[] createShortcutIntents(
+ int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId);
public abstract void addListener(@NonNull ShortcutChangeListener listener);
@@ -67,10 +68,4 @@
public abstract boolean hasShortcutHostPermission(int launcherUserId,
@NonNull String callingPackage);
-
- /**
- * Called by AM when the system locale changes *within the AM lock*. ABSOLUTELY do not take
- * any locks in this method.
- */
- public abstract void onSystemLocaleChangedNoLock();
}
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index dd3a36c..6cd8403 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -28,8 +28,8 @@
*/
public class UserInfo implements Parcelable {
- /** 8 bits for user type */
- public static final int FLAG_MASK_USER_TYPE = 0x000000FF;
+ /** 16 bits for user type */
+ public static final int FLAG_MASK_USER_TYPE = 0x0000FFFF;
/**
* *************************** NOTE ***************************
@@ -87,6 +87,11 @@
*/
public static final int FLAG_EPHEMERAL = 0x00000100;
+ /**
+ * User is for demo purposes only and can be removed at any time.
+ */
+ public static final int FLAG_DEMO = 0x00000200;
+
public static final int NO_PROFILE_GROUP_ID = UserHandle.USER_NULL;
public int id;
@@ -153,6 +158,10 @@
return (flags & FLAG_INITIALIZED) == FLAG_INITIALIZED;
}
+ public boolean isDemo() {
+ return (flags & FLAG_DEMO) == FLAG_DEMO;
+ }
+
/**
* Returns true if the user is a split system user.
* <p>If {@link UserManager#isSplitSystemUser split system user mode} is not enabled,
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 6f43d99..b2d518c 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1543,27 +1543,41 @@
* @hide
*/
public static String localesToResourceQualifier(LocaleList locs) {
- StringBuilder sb = new StringBuilder();
+ final StringBuilder sb = new StringBuilder();
for (int i = 0; i < locs.size(); i++) {
- Locale loc = locs.get(i);
- boolean l = (loc.getLanguage().length() != 0);
- boolean c = (loc.getCountry().length() != 0);
- boolean s = (loc.getScript().length() != 0);
- boolean v = (loc.getVariant().length() != 0);
- // TODO: take script and extensions into account
- if (l) {
- if (sb.length() != 0) {
- sb.append(",");
- }
+ final Locale loc = locs.get(i);
+ final int l = loc.getLanguage().length();
+ if (l == 0) {
+ continue;
+ }
+ final int s = loc.getScript().length();
+ final int c = loc.getCountry().length();
+ final int v = loc.getVariant().length();
+ // We ignore locale extensions, since they are not supported by AAPT
+
+ if (sb.length() != 0) {
+ sb.append(",");
+ }
+ if (l == 2 && s == 0 && (c == 0 || c == 2) && v == 0) {
+ // Traditional locale format: xx or xx-rYY
sb.append(loc.getLanguage());
- if (c) {
+ if (c == 2) {
sb.append("-r").append(loc.getCountry());
- if (s) {
- sb.append("-s").append(loc.getScript());
- if (v) {
- sb.append("-v").append(loc.getVariant());
- }
- }
+ }
+ } else {
+ sb.append("b+");
+ sb.append(loc.getLanguage());
+ if (s != 0) {
+ sb.append("+");
+ sb.append(loc.getScript());
+ }
+ if (c != 0) {
+ sb.append("+");
+ sb.append(loc.getCountry());
+ }
+ if (v != 0) {
+ sb.append("+");
+ sb.append(loc.getVariant());
}
}
}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 8d3940c..ad11307 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -201,12 +201,16 @@
* Create a new Resources object on top of an existing set of assets in an
* AssetManager.
*
+ * @deprecated Resources should not be constructed by apps.
+ * See {@link android.content.Context#createConfigurationContext(Configuration)}.
+ *
* @param assets Previously created AssetManager.
* @param metrics Current display metrics to consider when
* selecting/computing resource values.
* @param config Desired device configuration to consider when
* selecting/computing resource values (optional).
*/
+ @Deprecated
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
this(null);
mResourcesImpl = new ResourcesImpl(assets, metrics, config, new DisplayAdjustments());
@@ -1762,7 +1766,10 @@
/**
* Store the newly updated configuration.
+ *
+ * @deprecated See {@link android.content.Context#createConfigurationContext(Configuration)}.
*/
+ @Deprecated
public void updateConfiguration(Configuration config, DisplayMetrics metrics) {
updateConfiguration(config, metrics, null);
}
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 38279a4..4b21187 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.camera2.params.OutputConfiguration;
import android.os.Handler;
import android.view.Surface;
@@ -220,6 +221,53 @@
public abstract void tearDown(@NonNull Surface surface) throws CameraAccessException;
/**
+ * <p>
+ * Finish the deferred output configurations where the output Surface was not configured before.
+ * </p>
+ * <p>
+ * For camera use cases where a preview and other output configurations need to be configured,
+ * it can take some time for the preview Surface to be ready (e.g., if the preview Surface is
+ * obtained from {@link android.view.SurfaceView}, the SurfaceView is ready after the UI layout
+ * is done, then it takes some time to get the preview Surface).
+ * </p>
+ * <p>
+ * To speed up camera startup time, the application can configure the
+ * {@link CameraCaptureSession} with the desired preview size, and defer the preview output
+ * configuration until the Surface is ready. After the {@link CameraCaptureSession} is created
+ * successfully with this deferred configuration and other normal configurations, the
+ * application can submit requests that don't include deferred output Surfaces. Once the
+ * deferred Surface is ready, the application can set the Surface to the same deferred output
+ * configuration with the {@link OutputConfiguration#setDeferredSurface} method, and then finish
+ * the deferred output configuration via this method, before it can submit requests with this
+ * output target.
+ * </p>
+ * <p>
+ * The output Surfaces included by this list of deferred {@link OutputConfiguration
+ * OutputConfigurations} can be used as {@link CaptureRequest} targets as soon as this call
+ * returns;
+ * </p>
+ * <p>
+ * This method is not supported by Legacy devices.
+ * </p>
+ *
+ * @param deferredOutputConfigs a list of {@link OutputConfiguration OutputConfigurations} that
+ * have had {@link OutputConfiguration#setDeferredSurface setDeferredSurface} invoked
+ * with a valid output Surface.
+ * @throws CameraAccessException if the camera device is no longer connected or has encountered
+ * a fatal error.
+ * @throws IllegalStateException if this session is no longer active, either because the session
+ * was explicitly closed, a new session has been created or the camera device has
+ * been closed. Or if this output configuration was already finished with the
+ * included surface before.
+ * @throws IllegalArgumentException for invalid output configurations, including ones where the
+ * source of the Surface is no longer valid or the Surface is from a unsupported
+ * source.
+ * @hide
+ */
+ public abstract void finishDeferredConfiguration(
+ List<OutputConfiguration> deferredOutputConfigs) throws CameraAccessException;
+
+ /**
* <p>Submit a request for an image to be captured by the camera device.</p>
*
* <p>The request defines all the parameters for capturing the single image,
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 3917bfa..145b1d0 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -267,6 +267,10 @@
* @param cameraId The unique identifier of the camera device to open
* @param callback The callback for the camera. Must not be null.
* @param handler The handler to invoke the callback on. Must not be null.
+ * @param uid The UID of the application actually opening the camera.
+ * Must be USE_CALLING_UID unless the caller is a service
+ * that is trusted to open the device on behalf of an
+ * application and to forward the real UID.
*
* @throws CameraAccessException if the camera is disabled by device policy,
* too many camera devices are already open, or the cameraId does not match
@@ -281,7 +285,7 @@
* @see android.app.admin.DevicePolicyManager#setCameraDisabled
*/
private CameraDevice openCameraDeviceUserAsync(String cameraId,
- CameraDevice.StateCallback callback, Handler handler)
+ CameraDevice.StateCallback callback, Handler handler, final int uid)
throws CameraAccessException {
CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
CameraDevice device = null;
@@ -317,7 +321,7 @@
"Camera service is currently unavailable");
}
cameraUser = cameraService.connectDevice(callbacks, id,
- mContext.getOpPackageName(), USE_CALLING_UID);
+ mContext.getOpPackageName(), uid);
} else {
// Use legacy camera implementation for HAL1 devices
Log.i(TAG, "Using legacy camera HAL.");
@@ -434,6 +438,29 @@
@NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
throws CameraAccessException {
+ openCameraForUid(cameraId, callback, handler, USE_CALLING_UID);
+ }
+
+ /**
+ * Open a connection to a camera with the given ID, on behalf of another application
+ * specified by clientUid.
+ *
+ * <p>The behavior of this method matches that of {@link #openCamera}, except that it allows
+ * the caller to specify the UID to use for permission/etc verification. This can only be
+ * done by services trusted by the camera subsystem to act on behalf of applications and
+ * to forward the real UID.</p>
+ *
+ * @param clientUid
+ * The UID of the application on whose behalf the camera is being opened.
+ * Must be USE_CALLING_UID unless the caller is a trusted service.
+ *
+ * @hide
+ */
+ public void openCameraForUid(@NonNull String cameraId,
+ @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler,
+ int clientUid)
+ throws CameraAccessException {
+
if (cameraId == null) {
throw new IllegalArgumentException("cameraId was null");
} else if (callback == null) {
@@ -447,7 +474,7 @@
}
}
- openCameraDeviceUserAsync(cameraId, callback, handler);
+ openCameraDeviceUserAsync(cameraId, callback, handler, clientUid);
}
/**
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index 6736d34..b10c341 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -24,6 +24,7 @@
import android.hardware.camera2.dispatch.DuckTypingDispatcher;
import android.hardware.camera2.dispatch.HandlerDispatcher;
import android.hardware.camera2.dispatch.InvokeDispatcher;
+import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.utils.TaskDrainer;
import android.hardware.camera2.utils.TaskSingleDrainer;
import android.os.Handler;
@@ -156,6 +157,12 @@
}
@Override
+ public void finishDeferredConfiguration(
+ List<OutputConfiguration> deferredOutputConfigs) throws CameraAccessException {
+ mDeviceImpl.finishDeferredConfig(deferredOutputConfigs);
+ }
+
+ @Override
public synchronized int capture(CaptureRequest request, CaptureCallback callback,
Handler handler) throws CameraAccessException {
if (request == null) {
@@ -279,23 +286,29 @@
}
@Override
- public synchronized void abortCaptures() throws CameraAccessException {
- checkNotClosed();
+ public void abortCaptures() throws CameraAccessException {
+ synchronized (this) {
+ checkNotClosed();
- if (DEBUG) {
- Log.v(TAG, mIdString + "abortCaptures");
+ if (DEBUG) {
+ Log.v(TAG, mIdString + "abortCaptures");
+ }
+
+ if (mAborting) {
+ Log.w(TAG, mIdString + "abortCaptures - Session is already aborting; doing nothing");
+ return;
+ }
+
+ mAborting = true;
+ mAbortDrainer.taskStarted();
}
- if (mAborting) {
- Log.w(TAG, mIdString + "abortCaptures - Session is already aborting; doing nothing");
- return;
+ synchronized (mDeviceImpl.mInterfaceLock) {
+ synchronized (this) {
+ mDeviceImpl.flush();
+ // The next BUSY -> IDLE set of transitions will mark the end of the abort.
+ }
}
-
- mAborting = true;
- mAbortDrainer.taskStarted();
-
- mDeviceImpl.flush();
- // The next BUSY -> IDLE set of transitions will mark the end of the abort.
}
@Override
@@ -323,78 +336,86 @@
* @see CameraCaptureSession#close
*/
@Override
- public synchronized void replaceSessionClose() {
- /*
- * In order for creating new sessions to be fast, the new session should be created
- * before the old session is closed.
- *
- * Otherwise the old session will always unconfigure if there is no new session to
- * replace it.
- *
- * Unconfiguring could add hundreds of milliseconds of delay. We could race and attempt
- * to skip unconfigure if a new session is created before the captures are all drained,
- * but this would introduce nondeterministic behavior.
- */
+ public void replaceSessionClose() {
+ synchronized (this) {
+ /*
+ * In order for creating new sessions to be fast, the new session should be created
+ * before the old session is closed.
+ *
+ * Otherwise the old session will always unconfigure if there is no new session to
+ * replace it.
+ *
+ * Unconfiguring could add hundreds of milliseconds of delay. We could race and attempt
+ * to skip unconfigure if a new session is created before the captures are all drained,
+ * but this would introduce nondeterministic behavior.
+ */
- if (DEBUG) Log.v(TAG, mIdString + "replaceSessionClose");
+ if (DEBUG) Log.v(TAG, mIdString + "replaceSessionClose");
- // Set up fast shutdown. Possible alternative paths:
- // - This session is active, so close() below starts the shutdown drain
- // - This session is mid-shutdown drain, and hasn't yet reached the idle drain listener.
- // - This session is already closed and has executed the idle drain listener, and
- // configureOutputsChecked(null) has already been called.
- //
- // Do not call configureOutputsChecked(null) going forward, since it would race with the
- // configuration for the new session. If it was already called, then we don't care, since it
- // won't get called again.
- mSkipUnconfigure = true;
-
+ // Set up fast shutdown. Possible alternative paths:
+ // - This session is active, so close() below starts the shutdown drain
+ // - This session is mid-shutdown drain, and hasn't yet reached the idle drain listener.
+ // - This session is already closed and has executed the idle drain listener, and
+ // configureOutputsChecked(null) has already been called.
+ //
+ // Do not call configureOutputsChecked(null) going forward, since it would race with the
+ // configuration for the new session. If it was already called, then we don't care,
+ // since it won't get called again.
+ mSkipUnconfigure = true;
+ }
close();
}
@Override
- public synchronized void close() {
+ public void close() {
+ synchronized (this) {
+ if (mClosed) {
+ if (DEBUG) Log.v(TAG, mIdString + "close - reentering");
+ return;
+ }
- if (mClosed) {
- if (DEBUG) Log.v(TAG, mIdString + "close - reentering");
- return;
+ if (DEBUG) Log.v(TAG, mIdString + "close - first time");
+
+ mClosed = true;
}
- if (DEBUG) Log.v(TAG, mIdString + "close - first time");
+ synchronized (mDeviceImpl.mInterfaceLock) {
+ synchronized (this) {
+ /*
+ * Flush out any repeating request. Since camera is closed, no new requests
+ * can be queued, and eventually the entire request queue will be drained.
+ *
+ * If the camera device was already closed, short circuit and do nothing; since
+ * no more internal device callbacks will fire anyway.
+ *
+ * Otherwise, once stopRepeating is done, wait for camera to idle, then unconfigure
+ * the camera. Once that's done, fire #onClosed.
+ */
+ try {
+ mDeviceImpl.stopRepeating();
+ } catch (IllegalStateException e) {
+ // OK: Camera device may already be closed, nothing else to do
- mClosed = true;
+ // TODO: Fire onClosed anytime we get the device onClosed or the ISE?
+ // or just suppress the ISE only and rely onClosed.
+ // Also skip any of the draining work if this is already closed.
- /*
- * Flush out any repeating request. Since camera is closed, no new requests
- * can be queued, and eventually the entire request queue will be drained.
- *
- * If the camera device was already closed, short circuit and do nothing; since
- * no more internal device callbacks will fire anyway.
- *
- * Otherwise, once stopRepeating is done, wait for camera to idle, then unconfigure the
- * camera. Once that's done, fire #onClosed.
- */
- try {
- mDeviceImpl.stopRepeating();
- } catch (IllegalStateException e) {
- // OK: Camera device may already be closed, nothing else to do
+ // Short-circuit; queue callback immediately and return
+ mStateCallback.onClosed(this);
+ return;
+ } catch (CameraAccessException e) {
+ // OK: close does not throw checked exceptions.
+ Log.e(TAG, mIdString + "Exception while stopping repeating: ", e);
- // TODO: Fire onClosed anytime we get the device onClosed or the ISE?
- // or just suppress the ISE only and rely onClosed.
- // Also skip any of the draining work if this is already closed.
-
- // Short-circuit; queue callback immediately and return
- mStateCallback.onClosed(this);
- return;
- } catch (CameraAccessException e) {
- // OK: close does not throw checked exceptions.
- Log.e(TAG, mIdString + "Exception while stopping repeating: ", e);
-
- // TODO: call onError instead of onClosed if this happens
+ // TODO: call onError instead of onClosed if this happens
+ }
+ }
}
- // If no sequences are pending, fire #onClosed immediately
- mSequenceDrainer.beginDrain();
+ synchronized (this) {
+ // If no sequences are pending, fire #onClosed immediately
+ mSequenceDrainer.beginDrain();
+ }
}
/**
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index 8cd1da5..1c8e124 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -21,6 +21,7 @@
import android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.utils.SurfaceUtils;
import android.os.Handler;
@@ -256,6 +257,12 @@
return mSessionImpl.isAborting();
}
+ @Override
+ public void finishDeferredConfiguration(List<OutputConfiguration> deferredOutputConfigs)
+ throws CameraAccessException {
+ mSessionImpl.finishDeferredConfiguration(deferredOutputConfigs);
+ }
+
private class WrapperCallback extends StateCallback {
private final StateCallback mCallback;
@@ -263,26 +270,32 @@
mCallback = callback;
}
+ @Override
public void onConfigured(CameraCaptureSession session) {
mCallback.onConfigured(CameraConstrainedHighSpeedCaptureSessionImpl.this);
}
+ @Override
public void onConfigureFailed(CameraCaptureSession session) {
mCallback.onConfigureFailed(CameraConstrainedHighSpeedCaptureSessionImpl.this);
}
+ @Override
public void onReady(CameraCaptureSession session) {
mCallback.onReady(CameraConstrainedHighSpeedCaptureSessionImpl.this);
}
+ @Override
public void onActive(CameraCaptureSession session) {
mCallback.onActive(CameraConstrainedHighSpeedCaptureSessionImpl.this);
}
+ @Override
public void onClosed(CameraCaptureSession session) {
mCallback.onClosed(CameraConstrainedHighSpeedCaptureSessionImpl.this);
}
+ @Override
public void onSurfacePrepared(CameraCaptureSession session, Surface surface) {
mCallback.onSurfacePrepared(CameraConstrainedHighSpeedCaptureSessionImpl.this,
surface);
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 0cee114..ee8a6d7 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -407,7 +407,10 @@
int streamId = mConfiguredOutputs.keyAt(i);
OutputConfiguration outConfig = mConfiguredOutputs.valueAt(i);
- if (!outputs.contains(outConfig)) {
+ if (!outputs.contains(outConfig) || outConfig.isDeferredConfiguration()) {
+ // Always delete the deferred output configuration when the session
+ // is created, as the deferred output configuration doesn't have unique surface
+ // related identifies.
deleteList.add(streamId);
} else {
addSet.remove(outConfig); // Don't create a stream previously created
@@ -744,6 +747,37 @@
}
}
+ public void finishDeferredConfig(List<OutputConfiguration> deferredConfigs)
+ throws CameraAccessException {
+ if (deferredConfigs == null || deferredConfigs.size() == 0) {
+ throw new IllegalArgumentException("deferred config is null or empty");
+ }
+
+ synchronized(mInterfaceLock) {
+ for (OutputConfiguration config : deferredConfigs) {
+ int streamId = -1;
+ for (int i = 0; i < mConfiguredOutputs.size(); i++) {
+ // Have to use equal here, as createCaptureSessionByOutputConfigurations() and
+ // createReprocessableCaptureSessionByConfigurations() do a copy of the configs.
+ if (config.equals(mConfiguredOutputs.valueAt(i))) {
+ streamId = mConfiguredOutputs.keyAt(i);
+ break;
+ }
+ }
+ if (streamId == -1) {
+ throw new IllegalArgumentException("Deferred config is not part of this "
+ + "session");
+ }
+
+ if (config.getSurface() == null) {
+ throw new IllegalArgumentException("The deferred config for stream " + streamId
+ + " must have a non-null surface");
+ }
+ mRemoteDevice.setDeferredConfiguration(streamId, config);
+ }
+ }
+ }
+
public int capture(CaptureRequest request, CaptureCallback callback, Handler handler)
throws CameraAccessException {
if (DEBUG) {
@@ -2005,6 +2039,7 @@
*
* <p> Handle binder death for ICameraDeviceUser. Trigger onError.</p>
*/
+ @Override
public void binderDied() {
Log.w(TAG, "CameraDevice " + mCameraId + " died unexpectedly");
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index ef5f6d7..d77f60b 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -215,5 +215,14 @@
}
}
+ public void setDeferredConfiguration(int streamId, OutputConfiguration deferredConfig)
+ throws CameraAccessException {
+ try {
+ mRemoteDevice.setDeferredConfiguration(streamId, deferredConfig);
+ } catch (Throwable t) {
+ CameraManager.throwAsPublicException(t);
+ throw new UnsupportedOperationException("Unexpected exception", t);
+ }
+ }
}
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index acbf214..b9e75ee 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -567,6 +567,13 @@
}
@Override
+ public void setDeferredConfiguration(int steamId, OutputConfiguration config) {
+ String err = "Set deferred configuration is not supported on legacy devices";
+ Log.e(TAG, err);
+ throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
+ }
+
+ @Override
public int createInputStream(int width, int height, int format) {
String err = "Creating input stream is not supported on legacy devices";
Log.e(TAG, err);
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 61b534b..69c00e9 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -20,6 +20,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.utils.HashCodeHelpers;
import android.hardware.camera2.utils.SurfaceUtils;
@@ -95,6 +97,21 @@
}
/**
+ * Unknown surface source type.
+ */
+ private final int SURFACE_TYPE_UNKNOWN = -1;
+
+ /**
+ * The surface is obtained from {@link android.view.SurfaceView}.
+ */
+ private final int SURFACE_TYPE_SURFACE_VIEW = 0;
+
+ /**
+ * The surface is obtained from {@link android.graphics.SurfaceTexture}.
+ */
+ private final int SURFACE_TYPE_SURFACE_TEXTURE = 1;
+
+ /**
* Create a new {@link OutputConfiguration} instance with a {@link Surface},
* with a surface group ID.
*
@@ -179,12 +196,110 @@
checkNotNull(surface, "Surface must not be null");
checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
mSurfaceGroupId = surfaceGroupId;
+ mSurfaceType = SURFACE_TYPE_UNKNOWN;
mSurface = surface;
mRotation = rotation;
mConfiguredSize = SurfaceUtils.getSurfaceSize(surface);
mConfiguredFormat = SurfaceUtils.getSurfaceFormat(surface);
mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(surface);
mConfiguredGenerationId = surface.getGenerationId();
+ mIsDeferredConfig = false;
+ }
+
+ /**
+ * Create a new {@link OutputConfiguration} instance, with desired Surface size and Surface
+ * source class.
+ * <p>
+ * This constructor takes an argument for desired Surface size and the Surface source class
+ * without providing the actual output Surface. This is used to setup a output configuration
+ * with a deferred Surface. The application can use this output configuration to create a
+ * session.
+ * </p>
+ * <p>
+ * However, the actual output Surface must be set via {@link #setDeferredSurface} and finish the
+ * deferred Surface configuration via {@link CameraCaptureSession#finishDeferredConfiguration}
+ * before submitting a request with this Surface target. The deferred Surface can only be
+ * obtained from either from {@link android.view.SurfaceView} by calling
+ * {@link android.view.SurfaceHolder#getSurface}, or from
+ * {@link android.graphics.SurfaceTexture} via
+ * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}).
+ * </p>
+ *
+ * @param surfaceSize Size for the deferred surface.
+ * @param klass a non-{@code null} {@link Class} object reference that indicates the source of
+ * this surface. Only {@link android.view.SurfaceHolder SurfaceHolder.class} and
+ * {@link android.graphics.SurfaceTexture SurfaceTexture.class} are supported.
+ * @hide
+ */
+ public <T> OutputConfiguration(@NonNull Size surfaceSize, @NonNull Class<T> klass) {
+ checkNotNull(klass, "surfaceSize must not be null");
+ checkNotNull(klass, "klass must not be null");
+ if (klass == android.view.SurfaceHolder.class) {
+ mSurfaceType = SURFACE_TYPE_SURFACE_VIEW;
+ } else if (klass == android.graphics.SurfaceTexture.class) {
+ mSurfaceType = SURFACE_TYPE_SURFACE_TEXTURE;
+ } else {
+ mSurfaceType = SURFACE_TYPE_UNKNOWN;
+ throw new IllegalArgumentException("Unknow surface source class type");
+ }
+
+ mSurfaceGroupId = SURFACE_GROUP_ID_NONE;
+ mSurface = null;
+ mRotation = ROTATION_0;
+ mConfiguredSize = surfaceSize;
+ mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE);
+ mConfiguredDataspace = StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
+ mConfiguredGenerationId = 0;
+ mIsDeferredConfig = true;
+ }
+
+ /**
+ * Check if this configuration has deferred configuration.
+ *
+ * <p>This will return true if the output configuration was constructed with surface deferred.
+ * It will return true even after the deferred surface is set later.</p>
+ *
+ * @return true if this configuration has deferred surface.
+ * @hide
+ */
+ public boolean isDeferredConfiguration() {
+ return mIsDeferredConfig;
+ }
+
+ /**
+ * Set the deferred surface to this OutputConfiguration.
+ *
+ * <p>
+ * The deferred surface must be obtained from either from {@link android.view.SurfaceView} by
+ * calling {@link android.view.SurfaceHolder#getSurface}, or from
+ * {@link android.graphics.SurfaceTexture} via
+ * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}). After the deferred
+ * surface is set, the application must finish the deferred surface configuration via
+ * {@link CameraCaptureSession#finishDeferredConfiguration} before submitting a request with
+ * this surface target.
+ * </p>
+ *
+ * @param surface The deferred surface to be set.
+ * @throws IllegalArgumentException if the Surface is invalid.
+ * @throws IllegalStateException if a Surface was already set to this deferred
+ * OutputConfiguration.
+ * @hide
+ */
+ public void setDeferredSurface(@NonNull Surface surface) {
+ checkNotNull(surface, "Surface must not be null");
+ if (mSurface != null) {
+ throw new IllegalStateException("Deferred surface is already set!");
+ }
+
+ // This will throw IAE is the surface was abandoned.
+ Size surfaceSize = SurfaceUtils.getSurfaceSize(surface);
+ if (!surfaceSize.equals(mConfiguredSize)) {
+ Log.w(TAG, "Deferred surface size " + surfaceSize +
+ " is different with pre-configured size " + mConfiguredSize +
+ ", the pre-configured size will be used.");
+ }
+
+ mSurface = surface;
}
/**
@@ -203,10 +318,12 @@
this.mSurface = other.mSurface;
this.mRotation = other.mRotation;
this.mSurfaceGroupId = other.mSurfaceGroupId;
+ this.mSurfaceType = other.mSurfaceType;
this.mConfiguredDataspace = other.mConfiguredDataspace;
this.mConfiguredFormat = other.mConfiguredFormat;
this.mConfiguredSize = other.mConfiguredSize;
this.mConfiguredGenerationId = other.mConfiguredGenerationId;
+ this.mIsDeferredConfig = other.mIsDeferredConfig;
}
/**
@@ -215,16 +332,30 @@
private OutputConfiguration(@NonNull Parcel source) {
int rotation = source.readInt();
int surfaceSetId = source.readInt();
+ int surfaceType = source.readInt();
+ int width = source.readInt();
+ int height = source.readInt();
Surface surface = Surface.CREATOR.createFromParcel(source);
- checkNotNull(surface, "Surface must not be null");
checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
mSurfaceGroupId = surfaceSetId;
mSurface = surface;
mRotation = rotation;
- mConfiguredSize = SurfaceUtils.getSurfaceSize(mSurface);
- mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurface);
- mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(mSurface);
- mConfiguredGenerationId = mSurface.getGenerationId();
+ if (surface != null) {
+ mSurfaceType = SURFACE_TYPE_UNKNOWN;
+ mConfiguredSize = SurfaceUtils.getSurfaceSize(mSurface);
+ mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurface);
+ mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(mSurface);
+ mConfiguredGenerationId = mSurface.getGenerationId();
+ mIsDeferredConfig = true;
+ } else {
+ mSurfaceType = surfaceType;
+ mConfiguredSize = new Size(width, height);
+ mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE);
+ mConfiguredGenerationId = 0;
+ mConfiguredDataspace =
+ StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
+ mIsDeferredConfig = false;
+ }
}
/**
@@ -291,7 +422,12 @@
}
dest.writeInt(mRotation);
dest.writeInt(mSurfaceGroupId);
- mSurface.writeToParcel(dest, flags);
+ dest.writeInt(mSurfaceType);
+ dest.writeInt(mConfiguredSize.getWidth());
+ dest.writeInt(mConfiguredSize.getHeight());
+ if (mSurface != null) {
+ mSurface.writeToParcel(dest, flags);
+ }
}
/**
@@ -311,13 +447,20 @@
return true;
} else if (obj instanceof OutputConfiguration) {
final OutputConfiguration other = (OutputConfiguration) obj;
+ boolean iSSurfaceEqual = mSurface == other.mSurface &&
+ mConfiguredGenerationId == other.mConfiguredGenerationId ;
+ if (mIsDeferredConfig) {
+ Log.i(TAG, "deferred config has the same surface");
+ iSSurfaceEqual = true;
+ }
return mRotation == other.mRotation &&
- mSurface == other.mSurface &&
- mConfiguredGenerationId == other.mConfiguredGenerationId &&
+ iSSurfaceEqual&&
mConfiguredSize.equals(other.mConfiguredSize) &&
mConfiguredFormat == other.mConfiguredFormat &&
mConfiguredDataspace == other.mConfiguredDataspace &&
- mSurfaceGroupId == other.mSurfaceGroupId;
+ mSurfaceGroupId == other.mSurfaceGroupId &&
+ mSurfaceType == other.mSurfaceType &&
+ mIsDeferredConfig == other.mIsDeferredConfig;
}
return false;
}
@@ -327,15 +470,26 @@
*/
@Override
public int hashCode() {
+ // Need ensure that the hashcode remains unchanged after set a deferred surface. Otherwise
+ // The deferred output configuration will be lost in the camera streammap after the deferred
+ // surface is set.
+ if (mIsDeferredConfig) {
+ return HashCodeHelpers.hashCode(
+ mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace,
+ mSurfaceGroupId, mSurfaceType);
+ }
+
return HashCodeHelpers.hashCode(
mRotation, mSurface.hashCode(), mConfiguredGenerationId,
mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace, mSurfaceGroupId);
}
private static final String TAG = "OutputConfiguration";
- private final Surface mSurface;
+ private Surface mSurface;
private final int mRotation;
- private int mSurfaceGroupId;
+ private final int mSurfaceGroupId;
+ // Surface source type, this is only used by the deferred surface configuration objects.
+ private final int mSurfaceType;
// The size, format, and dataspace of the surface when OutputConfiguration is created.
private final Size mConfiguredSize;
@@ -343,4 +497,6 @@
private final int mConfiguredDataspace;
// Surface generation ID to distinguish changes to Surface native internals
private final int mConfiguredGenerationId;
+ // Flag indicating if this config has deferred surface.
+ private final boolean mIsDeferredConfig;
}
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 93da3e5..826eb74 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -354,9 +354,9 @@
}
}
- public void requestColorTransform(int displayId, int colorTransformId) {
+ public void requestColorMode(int displayId, int colorMode) {
try {
- mDm.requestColorTransform(displayId, colorTransformId);
+ mDm.requestColorMode(displayId, colorMode);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 8a1abf1..f696c8d 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -59,8 +59,8 @@
// No permissions required.
WifiDisplayStatus getWifiDisplayStatus();
- // Requires CONFIGURE_DISPLAY_COLOR_TRANSFORM
- void requestColorTransform(int displayId, int colorTransformId);
+ // Requires CONFIGURE_DISPLAY_COLOR_MODE
+ void requestColorMode(int displayId, int colorMode);
// Requires CAPTURE_VIDEO_OUTPUT, CAPTURE_SECURE_VIDEO_OUTPUT, or an appropriate
// MediaProjection token for certain combinations of flags.
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 1ff2e8a..f17fd55 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -259,6 +259,7 @@
public static class AuthenticationResult {
private Fingerprint mFingerprint;
private CryptoObject mCryptoObject;
+ private int mUserId;
/**
* Authentication result
@@ -267,9 +268,10 @@
* @param fingerprint the recognized fingerprint data, if allowed.
* @hide
*/
- public AuthenticationResult(CryptoObject crypto, Fingerprint fingerprint) {
+ public AuthenticationResult(CryptoObject crypto, Fingerprint fingerprint, int userId) {
mCryptoObject = crypto;
mFingerprint = fingerprint;
+ mUserId = userId;
}
/**
@@ -286,6 +288,12 @@
* @hide
*/
public Fingerprint getFingerprint() { return mFingerprint; }
+
+ /**
+ * Obtain the userId for which this fingerprint was authenticated.
+ * @hide
+ */
+ public int getUserId() { return mUserId; }
};
/**
@@ -792,7 +800,7 @@
sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */);
break;
case MSG_AUTHENTICATION_SUCCEEDED:
- sendAuthenticatedSucceeded((Fingerprint) msg.obj);
+ sendAuthenticatedSucceeded((Fingerprint) msg.obj, msg.arg1 /* userId */);
break;
case MSG_AUTHENTICATION_FAILED:
sendAuthenticatedFailed();
@@ -840,9 +848,10 @@
}
}
- private void sendAuthenticatedSucceeded(Fingerprint fp) {
+ private void sendAuthenticatedSucceeded(Fingerprint fp, int userId) {
if (mAuthenticationCallback != null) {
- final AuthenticationResult result = new AuthenticationResult(mCryptoObject, fp);
+ final AuthenticationResult result =
+ new AuthenticationResult(mCryptoObject, fp, userId);
mAuthenticationCallback.onAuthenticationSucceeded(result);
}
}
@@ -981,8 +990,8 @@
}
@Override // binder call
- public void onAuthenticationSucceeded(long deviceId, Fingerprint fp) {
- mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, fp).sendToTarget();
+ public void onAuthenticationSucceeded(long deviceId, Fingerprint fp, int userId) {
+ mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, 0, fp).sendToTarget();
}
@Override // binder call
diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
index 57a429f..b024b29 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
@@ -26,7 +26,7 @@
oneway interface IFingerprintServiceReceiver {
void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining);
void onAcquired(long deviceId, int acquiredInfo);
- void onAuthenticationSucceeded(long deviceId, in Fingerprint fp);
+ void onAuthenticationSucceeded(long deviceId, in Fingerprint fp, int userId);
void onAuthenticationFailed(long deviceId);
void onError(long deviceId, int error);
void onRemoved(long deviceId, int fingerId, int groupId);
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java
index 10fc8e6..01a5404 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/core/java/android/hardware/input/InputManagerInternal.java
@@ -59,4 +59,9 @@
* @param deviceId The id of input device.
*/
public abstract void toggleCapsLock(int deviceId);
+
+ /**
+ * Set whether the input stack should deliver pulse gesture events when the device is asleep.
+ */
+ public abstract void setPulseGestureEnabled(boolean enabled);
}
diff --git a/core/java/android/hardware/location/ContextHubService.java b/core/java/android/hardware/location/ContextHubService.java
index 43e596f..062c958 100644
--- a/core/java/android/hardware/location/ContextHubService.java
+++ b/core/java/android/hardware/location/ContextHubService.java
@@ -16,6 +16,11 @@
package android.hardware.location;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -53,10 +58,14 @@
private static final int PRE_LOADED_APP_MEM_REQ = 0;
private static final int MSG_HEADER_SIZE = 4;
- private static final int MSG_FIELD_TYPE = 0;
- private static final int MSG_FIELD_VERSION = 1;
- private static final int MSG_FIELD_HUB_HANDLE = 2;
- private static final int MSG_FIELD_APP_INSTANCE = 3;
+ private static final int HEADER_FIELD_MSG_TYPE = 0;
+ private static final int HEADER_FIELD_MSG_VERSION = 1;
+ private static final int HEADER_FIELD_HUB_HANDLE = 2;
+ private static final int HEADER_FIELD_APP_INSTANCE = 3;
+
+ private static final int HEADER_FIELD_LOAD_APP_ID_LO = MSG_HEADER_SIZE;
+ private static final int HEADER_FIELD_LOAD_APP_ID_HI = MSG_HEADER_SIZE + 1;
+ private static final int MSG_LOAD_APP_HEADER_SIZE = MSG_HEADER_SIZE + 2;
private static final int OS_APP_INSTANCE = -1;
@@ -146,15 +155,23 @@
return -1;
}
- int[] msgHeader = new int[MSG_HEADER_SIZE];
- msgHeader[MSG_FIELD_HUB_HANDLE] = contextHubHandle;
- msgHeader[MSG_FIELD_APP_INSTANCE] = OS_APP_INSTANCE;
- msgHeader[MSG_FIELD_VERSION] = 0;
- msgHeader[MSG_FIELD_TYPE] = MSG_LOAD_NANO_APP;
+ int[] msgHeader = new int[MSG_LOAD_APP_HEADER_SIZE];
+ msgHeader[HEADER_FIELD_HUB_HANDLE] = contextHubHandle;
+ msgHeader[HEADER_FIELD_APP_INSTANCE] = OS_APP_INSTANCE;
+ msgHeader[HEADER_FIELD_MSG_VERSION] = 0;
+ msgHeader[HEADER_FIELD_MSG_TYPE] = MSG_LOAD_NANO_APP;
- if (nativeSendMessage(msgHeader, app.getAppBinary()) != 0) {
+ long appId = app.getAppId();
+
+ msgHeader[HEADER_FIELD_LOAD_APP_ID_LO] = (int)(appId & 0xFFFFFFFF);
+ msgHeader[HEADER_FIELD_LOAD_APP_ID_HI] = (int)((appId >> 32) & 0xFFFFFFFF);
+
+ int errVal = nativeSendMessage(msgHeader, app.getAppBinary());
+ if (errVal != 0) {
+ Log.e(TAG, "Send Message returns error" + contextHubHandle);
return -1;
}
+
// Do not add an entry to mNanoAppInstance Hash yet. The HAL may reject the app
return 0;
}
@@ -169,12 +186,14 @@
// Call Native interface here
int[] msgHeader = new int[MSG_HEADER_SIZE];
- msgHeader[MSG_FIELD_HUB_HANDLE] = ANY_HUB;
- msgHeader[MSG_FIELD_APP_INSTANCE] = OS_APP_INSTANCE;
- msgHeader[MSG_FIELD_VERSION] = 0;
- msgHeader[MSG_FIELD_TYPE] = MSG_UNLOAD_NANO_APP;
+ msgHeader[HEADER_FIELD_HUB_HANDLE] = ANY_HUB;
+ msgHeader[HEADER_FIELD_APP_INSTANCE] = nanoAppInstanceHandle;
+ msgHeader[HEADER_FIELD_MSG_VERSION] = 0;
+ msgHeader[HEADER_FIELD_MSG_TYPE] = MSG_UNLOAD_NANO_APP;
- if (nativeSendMessage(msgHeader, null) != 0) {
+ byte msg[] = new byte[0];
+
+ if (nativeSendMessage(msgHeader, msg) != 0) {
return -1;
}
@@ -222,10 +241,10 @@
checkPermissions();
int[] msgHeader = new int[MSG_HEADER_SIZE];
- msgHeader[MSG_FIELD_HUB_HANDLE] = hubHandle;
- msgHeader[MSG_FIELD_APP_INSTANCE] = nanoAppHandle;
- msgHeader[MSG_FIELD_VERSION] = msg.getVersion();
- msgHeader[MSG_FIELD_TYPE] = msg.getMsgType();
+ msgHeader[HEADER_FIELD_HUB_HANDLE] = hubHandle;
+ msgHeader[HEADER_FIELD_APP_INSTANCE] = nanoAppHandle;
+ msgHeader[HEADER_FIELD_MSG_VERSION] = msg.getVersion();
+ msgHeader[HEADER_FIELD_MSG_TYPE] = msg.getMsgType();
return nativeSendMessage(msgHeader, msg.getData());
}
@@ -269,15 +288,17 @@
Log.v(TAG, "No message callbacks registered.");
return 0;
}
- ContextHubMessage message =
- new ContextHubMessage(header[MSG_FIELD_TYPE], header[MSG_FIELD_VERSION], data);
+
+ ContextHubMessage msg = new ContextHubMessage(header[HEADER_FIELD_MSG_TYPE],
+ header[HEADER_FIELD_MSG_VERSION],
+ data);
for (int i = 0; i < callbacksCount; ++i) {
IContextHubCallback callback = mCallbacksList.getBroadcastItem(i);
try {
callback.onMessageReceipt(
- header[MSG_FIELD_HUB_HANDLE],
- header[MSG_FIELD_APP_INSTANCE],
- message);
+ header[HEADER_FIELD_HUB_HANDLE],
+ header[HEADER_FIELD_APP_INSTANCE],
+ msg);
} catch (RemoteException e) {
Log.i(TAG, "Exception (" + e + ") calling remote callback (" + callback + ").");
continue;
@@ -308,12 +329,20 @@
return 0;
}
+ private int deleteAppInstance(int appInstanceHandle) {
+ if (mNanoAppHash.remove(appInstanceHandle) == null) {
+ return -1;
+ }
+
+ return 0;
+ }
+
private void sendVrStateChangeMessageToApp(NanoAppInstanceInfo app, boolean vrModeEnabled) {
int[] msgHeader = new int[MSG_HEADER_SIZE];
- msgHeader[MSG_FIELD_TYPE] = 0;
- msgHeader[MSG_FIELD_VERSION] = 0;
- msgHeader[MSG_FIELD_HUB_HANDLE] = ANY_HUB;
- msgHeader[MSG_FIELD_APP_INSTANCE] = app.getHandle();
+ msgHeader[HEADER_FIELD_MSG_TYPE] = 0;
+ msgHeader[HEADER_FIELD_MSG_VERSION] = 0;
+ msgHeader[HEADER_FIELD_HUB_HANDLE] = ANY_HUB;
+ msgHeader[HEADER_FIELD_APP_INSTANCE] = app.getHandle();
byte[] data = new byte[1];
data[0] = (byte) ((vrModeEnabled) ? 1 : 0);
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index ac968c8..629db06 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -21,6 +21,7 @@
import android.app.PendingIntent;
import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -476,6 +477,26 @@
}
/**
+ * Grants permission to specified package for USB device without showing system dialog.
+ * Only system components can call this function, as it requires the MANAGE_USB permission.
+ * @param device to request permissions for
+ * @param packageName of package to grant permissions
+ *
+ * {@hide}
+ */
+ public void grantPermission(UsbDevice device, String packageName) {
+ try {
+ int uid = mContext.getPackageManager()
+ .getPackageUidAsUser(packageName, mContext.getUserId());
+ mService.grantDevicePermission(device, uid);
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Package " + packageName + " not found.", e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns true if the specified USB function is currently enabled when in device mode.
* <p>
* USB functions represent interfaces which are published to the host to access
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index 3531926..29177b6 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -16,11 +16,14 @@
package android.inputmethodservice;
+import android.annotation.NonNull;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodSession;
@@ -208,7 +211,7 @@
*
* @param event The motion event being received.
* @return True if the event was handled in this function, false otherwise.
- * @see View#onTrackballEvent
+ * @see android.view.View#onTrackballEvent(MotionEvent)
*/
public boolean onTrackballEvent(MotionEvent event) {
return false;
@@ -219,9 +222,30 @@
*
* @param event The motion event being received.
* @return True if the event was handled in this function, false otherwise.
- * @see View#onGenericMotionEvent
+ * @see android.view.View#onGenericMotionEvent(MotionEvent)
*/
public boolean onGenericMotionEvent(MotionEvent event) {
return false;
}
+
+ /**
+ * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
+ * permission to the content.
+ *
+ * <p>Default implementation does nothing.</p>
+ *
+ * @param inputContentInfo Content to be temporarily exposed from the input method to the
+ * application.
+ * This cannot be {@code null}.
+ * @param inputConnection {@link InputConnection} with which
+ * {@link InputConnection#commitContent(InputContentInfo, int, android.os.Bundle)} will be
+ * called.
+ * @return {@code false} if we cannot allow a temporary access permission.
+ * @hide
+ */
+ public void exposeContent(@NonNull InputContentInfo inputContentInfo,
+ @NonNull InputConnection inputConnection) {
+ return;
+ }
+
}
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index cc71a9c..167d5a0 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -168,7 +168,7 @@
int missingMethods = msg.arg1;
IInputContext inputContext = (IInputContext)args.arg1;
InputConnection ic = inputContext != null
- ? new InputConnectionWrapper(inputContext, missingMethods) : null;
+ ? new InputConnectionWrapper(mTarget, inputContext, missingMethods) : null;
EditorInfo info = (EditorInfo)args.arg2;
info.makeCompatible(mTargetSdkVersion);
inputMethod.startInput(ic, info);
@@ -180,7 +180,7 @@
int missingMethods = msg.arg1;
IInputContext inputContext = (IInputContext)args.arg1;
InputConnection ic = inputContext != null
- ? new InputConnectionWrapper(inputContext, missingMethods) : null;
+ ? new InputConnectionWrapper(mTarget, inputContext, missingMethods) : null;
EditorInfo info = (EditorInfo)args.arg2;
info.makeCompatible(mTargetSdkVersion);
inputMethod.restartInput(ic, info);
@@ -251,7 +251,7 @@
public void bindInput(InputBinding binding) {
// This IInputContext is guaranteed to implement all the methods.
final int missingMethodFlags = 0;
- InputConnection ic = new InputConnectionWrapper(
+ InputConnection ic = new InputConnectionWrapper(mTarget,
IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags);
InputBinding nu = new InputBinding(ic, binding);
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu));
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 4799773..fede77d 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -23,6 +23,7 @@
import android.annotation.DrawableRes;
import android.annotation.IntDef;
import android.annotation.MainThread;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.Dialog;
import android.content.Context;
@@ -65,6 +66,7 @@
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
@@ -2598,6 +2600,29 @@
}
/**
+ * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
+ * permission to the content.
+ *
+ * @param inputContentInfo Content to be temporarily exposed from the input method to the
+ * application.
+ * This cannot be {@code null}.
+ * @param inputConnection {@link InputConnection} with which
+ * {@link InputConnection#commitContent(InputContentInfo, Bundle)} will be called.
+ * @hide
+ */
+ @Override
+ public final void exposeContent(@NonNull InputContentInfo inputContentInfo,
+ @NonNull InputConnection inputConnection) {
+ if (inputConnection == null) {
+ return;
+ }
+ if (getCurrentInputConnection() != inputConnection) {
+ return;
+ }
+ mImm.exposeContent(mToken, inputContentInfo, getCurrentInputEditorInfo());
+ }
+
+ /**
* Performs a dump of the InputMethodService's internal state. Override
* to add your own information to the dump.
*/
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index a45e6f5..3c2ac67 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1035,6 +1035,26 @@
}
/**
+ * Request that this callback be invoked at ConnectivityService's earliest
+ * convenience with the current satisfying network's LinkProperties.
+ * If no such network exists no callback invocation is performed.
+ *
+ * The callback must have been registered with #requestNetwork() or
+ * #registerDefaultNetworkCallback(); callbacks registered with
+ * registerNetworkCallback() are not specific to any particular Network so
+ * do not cause any updates.
+ *
+ * @hide
+ */
+ public void requestLinkProperties(NetworkCallback networkCallback) {
+ try {
+ mService.requestLinkProperties(networkCallback.networkRequest);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Get the {@link android.net.NetworkCapabilities} for the given {@link Network}. This
* will return {@code null} if the network is unknown.
* <p>This method requires the caller to hold the permission
@@ -1052,6 +1072,26 @@
}
/**
+ * Request that this callback be invoked at ConnectivityService's earliest
+ * convenience with the current satisfying network's NetworkCapabilities.
+ * If no such network exists no callback invocation is performed.
+ *
+ * The callback must have been registered with #requestNetwork() or
+ * #registerDefaultNetworkCallback(); callbacks registered with
+ * registerNetworkCallback() are not specific to any particular Network so
+ * do not cause any updates.
+ *
+ * @hide
+ */
+ public void requestNetworkCapabilities(NetworkCallback networkCallback) {
+ try {
+ mService.requestNetworkCapabilities(networkCallback.networkRequest);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Gets the URL that should be used for resolving whether a captive portal is present.
* 1. This URL should respond with a 204 response to a GET request to indicate no captive
* portal is present.
@@ -3103,14 +3143,11 @@
throw new IllegalArgumentException("Invalid NetworkCallback");
}
try {
+ // CallbackHandler will release callback when receiving CALLBACK_RELEASED.
mService.releaseNetworkRequest(networkCallback.networkRequest);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
-
- synchronized (sNetworkCallback) {
- sNetworkCallback.remove(networkCallback.networkRequest);
- }
}
/**
diff --git a/core/java/android/net/ConnectivityMetricsLogger.java b/core/java/android/net/ConnectivityMetricsLogger.java
index d8cdde9..9a2d4e0 100644
--- a/core/java/android/net/ConnectivityMetricsLogger.java
+++ b/core/java/android/net/ConnectivityMetricsLogger.java
@@ -23,6 +23,8 @@
import android.os.ServiceManager;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
/** {@hide} */
@SystemApi
public class ConnectivityMetricsLogger {
@@ -33,28 +35,58 @@
// Component Tags
public static final int COMPONENT_TAG_CONNECTIVITY = 0;
- public static final int COMPONENT_TAG_BLUETOOTH = 1;
- public static final int COMPONENT_TAG_WIFI = 2;
- public static final int COMPONENT_TAG_TELECOM = 3;
- public static final int COMPONENT_TAG_TELEPHONY = 4;
-
- public static final int NUMBER_OF_COMPONENTS = 5;
+ public static final int COMPONENT_TAG_BLUETOOTH = 1;
+ public static final int COMPONENT_TAG_WIFI = 2;
+ public static final int COMPONENT_TAG_TELECOM = 3;
+ public static final int COMPONENT_TAG_TELEPHONY = 4;
+ public static final int NUMBER_OF_COMPONENTS = 5;
// Event Tag
public static final int TAG_SKIPPED_EVENTS = -1;
public static final String DATA_KEY_EVENTS_COUNT = "count";
- private IConnectivityMetricsLogger mService;
-
- private long mServiceUnblockedTimestampMillis = 0;
- private int mNumSkippedEvents = 0;
+ /** {@hide} */ protected IConnectivityMetricsLogger mService;
+ /** {@hide} */ protected volatile long mServiceUnblockedTimestampMillis;
+ private int mNumSkippedEvents;
public ConnectivityMetricsLogger() {
- mService = IConnectivityMetricsLogger.Stub.asInterface(ServiceManager.getService(
- CONNECTIVITY_METRICS_LOGGER_SERVICE));
+ // TODO: consider not initializing mService in constructor
+ this(IConnectivityMetricsLogger.Stub.asInterface(
+ ServiceManager.getService(CONNECTIVITY_METRICS_LOGGER_SERVICE)));
}
+ /** {@hide} */
+ @VisibleForTesting
+ public ConnectivityMetricsLogger(IConnectivityMetricsLogger service) {
+ mService = service;
+ }
+
+ /** {@hide} */
+ protected boolean checkLoggerService() {
+ if (mService != null) {
+ return true;
+ }
+ // Two threads racing here will write the same pointer because getService
+ // is idempotent once MetricsLoggerService is initialized.
+ mService = IConnectivityMetricsLogger.Stub.asInterface(
+ ServiceManager.getService(CONNECTIVITY_METRICS_LOGGER_SERVICE));
+ return mService != null;
+ }
+
+ /**
+ * Log a ConnectivityMetricsEvent.
+ *
+ * This method keeps track of skipped events when MetricsLoggerService throttles input events.
+ * It skips logging when MetricsLoggerService is active. When throttling ends, it logs a
+ * meta-event containing the number of events dropped. It is not safe to call this method
+ * concurrently from different threads.
+ *
+ * @param timestamp is the epoch timestamp of the event in ms.
+ * @param componentTag is the COMPONENT_* constant the event belongs to.
+ * @param eventTag is an event type constant whose meaning is specific to the component tag.
+ * @param data is a Parcelable instance representing the event.
+ */
public void logEvent(long timestamp, int componentTag, int eventTag, Parcelable data) {
if (mService == null) {
if (DBG) {
@@ -77,6 +109,12 @@
// Log number of skipped events
Bundle b = new Bundle();
b.putInt(DATA_KEY_EVENTS_COUNT, mNumSkippedEvents);
+
+ // Log the skipped event.
+ // TODO: Note that some of the clients push all states events into the server,
+ // If we lose some states logged here, we might mess up the statistics happened at the
+ // backend. One of the options is to introduce a non-skippable flag for important events
+ // that are logged.
skippedEventsEvent = new ConnectivityMetricsEvent(mServiceUnblockedTimestampMillis,
componentTag, TAG_SKIPPED_EVENTS, b);
@@ -104,7 +142,7 @@
}
}
} catch (RemoteException e) {
- Log.e(TAG, "Error logging event " + e.getMessage());
+ Log.e(TAG, "Error logging event", e);
}
}
@@ -121,8 +159,8 @@
public ConnectivityMetricsEvent[] getEvents(ConnectivityMetricsEvent.Reference reference) {
try {
return mService.getEvents(reference);
- } catch (RemoteException ex) {
- Log.e(TAG, "IConnectivityMetricsLogger.getEvents: " + ex);
+ } catch (RemoteException e) {
+ Log.e(TAG, "IConnectivityMetricsLogger.getEvents", e);
return null;
}
}
@@ -133,8 +171,8 @@
public boolean register(PendingIntent newEventsIntent) {
try {
return mService.register(newEventsIntent);
- } catch (RemoteException ex) {
- Log.e(TAG, "IConnectivityMetricsLogger.register: " + ex);
+ } catch (RemoteException e) {
+ Log.e(TAG, "IConnectivityMetricsLogger.register", e);
return false;
}
}
@@ -142,11 +180,10 @@
public boolean unregister(PendingIntent newEventsIntent) {
try {
mService.unregister(newEventsIntent);
- } catch (RemoteException ex) {
- Log.e(TAG, "IConnectivityMetricsLogger.unregister: " + ex);
+ return true;
+ } catch (RemoteException e) {
+ Log.e(TAG, "IConnectivityMetricsLogger.unregister", e);
return false;
}
-
- return true;
}
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 0d518cc..d48c155 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -156,6 +156,8 @@
void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
in PendingIntent operation);
+ void requestLinkProperties(in NetworkRequest networkRequest);
+ void requestNetworkCapabilities(in NetworkRequest networkRequest);
void releaseNetworkRequest(in NetworkRequest networkRequest);
void setAcceptUnvalidated(in Network network, boolean accept, boolean always);
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index 384ab1c..6e74f14 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -103,7 +103,7 @@
private boolean isIPv6ULA() {
if (address != null && address instanceof Inet6Address) {
byte[] bytes = address.getAddress();
- return ((bytes[0] & (byte)0xfc) == (byte)0xfc);
+ return ((bytes[0] & (byte)0xfe) == (byte)0xfc);
}
return false;
}
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index 5511a24..69f50a2 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -52,6 +52,15 @@
public boolean acceptUnvalidated;
/**
+ * Set to avoid surfacing the "Sign in to network" notification.
+ * if carrier receivers/apps are registered to handle the carrier-specific provisioning
+ * procedure, a carrier specific provisioning notification will be placed.
+ * only one notification should be displayed. This field is set based on
+ * which notification should be used for provisioning.
+ */
+ public boolean provisioningNotificationDisabled;
+
+ /**
* For mobile networks, this is the subscriber ID (such as IMSI).
*/
public String subscriberId;
@@ -65,6 +74,7 @@
explicitlySelected = nm.explicitlySelected;
acceptUnvalidated = nm.acceptUnvalidated;
subscriberId = nm.subscriberId;
+ provisioningNotificationDisabled = nm.provisioningNotificationDisabled;
}
}
@@ -79,6 +89,7 @@
out.writeInt(explicitlySelected ? 1 : 0);
out.writeInt(acceptUnvalidated ? 1 : 0);
out.writeString(subscriberId);
+ out.writeInt(provisioningNotificationDisabled ? 1 : 0);
}
public static final Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() {
@@ -89,6 +100,7 @@
networkMisc.explicitlySelected = in.readInt() != 0;
networkMisc.acceptUnvalidated = in.readInt() != 0;
networkMisc.subscriberId = in.readString();
+ networkMisc.provisioningNotificationDisabled = in.readInt() != 0;
return networkMisc;
}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 51c45e0..11b861a 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -18,7 +18,6 @@
import static android.content.pm.PackageManager.GET_SIGNATURES;
import static android.net.NetworkPolicy.CYCLE_NONE;
-import static android.text.format.Time.MONTH_DAY;
import android.content.Context;
import android.content.Intent;
@@ -27,12 +26,13 @@
import android.content.pm.Signature;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.text.format.Time;
import android.util.DebugUtils;
import com.google.android.collect.Sets;
+import java.util.Calendar;
import java.util.HashSet;
+import java.util.TimeZone;
/**
* Manager for creating and modifying network policy rules.
@@ -253,28 +253,18 @@
throw new IllegalArgumentException("Unable to compute boundary without cycleDay");
}
- final Time now = new Time(policy.cycleTimezone);
- now.set(currentTime);
+ final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(policy.cycleTimezone));
+ cal.setTimeInMillis(currentTime);
+ snapToCycleDay(cal, policy.cycleDay);
- // first, find cycle boundary for current month
- final Time cycle = new Time(now);
- cycle.hour = cycle.minute = cycle.second = 0;
- snapToCycleDay(cycle, policy.cycleDay);
-
- if (Time.compare(cycle, now) >= 0) {
- // cycle boundary is beyond now, use last cycle boundary; start by
- // pushing ourselves squarely into last month.
- final Time lastMonth = new Time(now);
- lastMonth.hour = lastMonth.minute = lastMonth.second = 0;
- lastMonth.monthDay = 1;
- lastMonth.month -= 1;
- lastMonth.normalize(true);
-
- cycle.set(lastMonth);
- snapToCycleDay(cycle, policy.cycleDay);
+ if (cal.getTimeInMillis() >= currentTime) {
+ // Cycle boundary is beyond now, use last cycle boundary
+ cal.set(Calendar.DAY_OF_MONTH, 1);
+ cal.add(Calendar.MONTH, -1);
+ snapToCycleDay(cal, policy.cycleDay);
}
- return cycle.toMillis(true);
+ return cal.getTimeInMillis();
}
/** {@hide} */
@@ -283,28 +273,18 @@
throw new IllegalArgumentException("Unable to compute boundary without cycleDay");
}
- final Time now = new Time(policy.cycleTimezone);
- now.set(currentTime);
+ final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(policy.cycleTimezone));
+ cal.setTimeInMillis(currentTime);
+ snapToCycleDay(cal, policy.cycleDay);
- // first, find cycle boundary for current month
- final Time cycle = new Time(now);
- cycle.hour = cycle.minute = cycle.second = 0;
- snapToCycleDay(cycle, policy.cycleDay);
-
- if (Time.compare(cycle, now) <= 0) {
- // cycle boundary is before now, use next cycle boundary; start by
- // pushing ourselves squarely into next month.
- final Time nextMonth = new Time(now);
- nextMonth.hour = nextMonth.minute = nextMonth.second = 0;
- nextMonth.monthDay = 1;
- nextMonth.month += 1;
- nextMonth.normalize(true);
-
- cycle.set(nextMonth);
- snapToCycleDay(cycle, policy.cycleDay);
+ if (cal.getTimeInMillis() <= currentTime) {
+ // Cycle boundary is before now, use next cycle boundary
+ cal.set(Calendar.DAY_OF_MONTH, 1);
+ cal.add(Calendar.MONTH, 1);
+ snapToCycleDay(cal, policy.cycleDay);
}
- return cycle.toMillis(true);
+ return cal.getTimeInMillis();
}
/**
@@ -313,16 +293,17 @@
*
* @hide
*/
- public static void snapToCycleDay(Time time, int cycleDay) {
- if (cycleDay > time.getActualMaximum(MONTH_DAY)) {
- // cycle day isn't valid this month; snap to last second of month
- time.month += 1;
- time.monthDay = 1;
- time.second = -1;
+ public static void snapToCycleDay(Calendar cal, int cycleDay) {
+ cal.set(Calendar.HOUR_OF_DAY, 0);
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.SECOND, 0);
+ if (cycleDay > cal.getActualMaximum(Calendar.DAY_OF_MONTH)) {
+ cal.set(Calendar.DAY_OF_MONTH, 1);
+ cal.add(Calendar.MONTH, 1);
+ cal.add(Calendar.SECOND, -1);
} else {
- time.monthDay = cycleDay;
+ cal.set(Calendar.DAY_OF_MONTH, cycleDay);
}
- time.normalize(true);
}
/**
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index f1edcbe..4501f7b 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* Defines a request for a network, made through {@link NetworkRequest.Builder} and used
* to request a network via {@link ConnectivityManager#requestNetwork} or listen for changes
@@ -47,15 +49,55 @@
public final int legacyType;
/**
+ * A NetworkRequest as used by the system can be one of three types:
+ *
+ * - LISTEN, for which the framework will issue callbacks about any
+ * and all networks that match the specified NetworkCapabilities,
+ *
+ * - REQUEST, capable of causing a specific network to be created
+ * first (e.g. a telephony DUN request), the framework will issue
+ * callbacks about the single, highest scoring current network
+ * (if any) that matches the specified NetworkCapabilities, or
+ *
+ * - TRACK_DEFAULT, a hybrid of the two designed such that the
+ * framework will issue callbacks for the single, highest scoring
+ * current network (if any) that matches the capabilities of the
+ * default Internet request (mDefaultRequest), but which cannot cause
+ * the framework to either create or retain the existence of any
+ * specific network.
+ *
+ * - The value NONE is used only by applications. When an application
+ * creates a NetworkRequest, it does not have a type; the type is set
+ * by the system depending on the method used to file the request
+ * (requestNetwork, registerNetworkCallback, etc.).
+ *
* @hide
*/
- public NetworkRequest(NetworkCapabilities nc, int legacyType, int rId) {
+ public static enum Type {
+ NONE,
+ LISTEN,
+ TRACK_DEFAULT,
+ REQUEST
+ };
+
+ /**
+ * The type of the request. This is only used by the system and is always NONE elsewhere.
+ *
+ * @hide
+ */
+ public final Type type;
+
+ /**
+ * @hide
+ */
+ public NetworkRequest(NetworkCapabilities nc, int legacyType, int rId, Type type) {
if (nc == null) {
throw new NullPointerException();
}
requestId = rId;
networkCapabilities = nc;
this.legacyType = legacyType;
+ this.type = type;
}
/**
@@ -65,6 +107,7 @@
networkCapabilities = new NetworkCapabilities(that.networkCapabilities);
requestId = that.requestId;
this.legacyType = that.legacyType;
+ this.type = that.type;
}
/**
@@ -90,7 +133,7 @@
final NetworkCapabilities nc = new NetworkCapabilities(mNetworkCapabilities);
nc.maybeMarkCapabilitiesRestricted();
return new NetworkRequest(nc, ConnectivityManager.TYPE_NONE,
- ConnectivityManager.REQUEST_ID_UNSET);
+ ConnectivityManager.REQUEST_ID_UNSET, Type.NONE);
}
/**
@@ -223,6 +266,7 @@
dest.writeParcelable(networkCapabilities, flags);
dest.writeInt(legacyType);
dest.writeInt(requestId);
+ dest.writeString(type.name());
}
public static final Creator<NetworkRequest> CREATOR =
new Creator<NetworkRequest>() {
@@ -230,7 +274,8 @@
NetworkCapabilities nc = (NetworkCapabilities)in.readParcelable(null);
int legacyType = in.readInt();
int requestId = in.readInt();
- NetworkRequest result = new NetworkRequest(nc, legacyType, requestId);
+ Type type = Type.valueOf(in.readString()); // IllegalArgumentException if invalid.
+ NetworkRequest result = new NetworkRequest(nc, legacyType, requestId, type);
return result;
}
public NetworkRequest[] newArray(int size) {
@@ -238,8 +283,36 @@
}
};
+ /**
+ * Returns true iff. the contained NetworkRequest is of type LISTEN.
+ *
+ * @hide
+ */
+ public boolean isListen() {
+ return type == Type.LISTEN;
+ }
+
+ /**
+ * Returns true iff. the contained NetworkRequest is one that:
+ *
+ * - should be associated with at most one satisfying network
+ * at a time;
+ *
+ * - should cause a network to be kept up if it is the best network
+ * which can satisfy the NetworkRequest.
+ *
+ * For full detail of how isRequest() is used for pairing Networks with
+ * NetworkRequests read rematchNetworkAndRequests().
+ *
+ * @hide
+ */
+ public boolean isRequest() {
+ return type == Type.TRACK_DEFAULT || type == Type.REQUEST;
+ }
+
public String toString() {
- return "NetworkRequest [ id=" + requestId + ", legacyType=" + legacyType +
+ return "NetworkRequest [ " + type + " id=" + requestId +
+ (legacyType != ConnectivityManager.TYPE_NONE ? ", legacyType=" + legacyType : "") +
", " + networkCapabilities.toString() + " ]";
}
@@ -248,13 +321,11 @@
NetworkRequest that = (NetworkRequest)obj;
return (that.legacyType == this.legacyType &&
that.requestId == this.requestId &&
- ((that.networkCapabilities == null && this.networkCapabilities == null) ||
- (that.networkCapabilities != null &&
- that.networkCapabilities.equals(this.networkCapabilities))));
+ that.type == this.type &&
+ Objects.equals(that.networkCapabilities, this.networkCapabilities));
}
public int hashCode() {
- return requestId + (legacyType * 1013) +
- (networkCapabilities.hashCode() * 1051);
+ return Objects.hash(requestId, legacyType, networkCapabilities, type);
}
}
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 141af3d..35e3065 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -45,13 +45,20 @@
public native static void attachDhcpFilter(FileDescriptor fd) throws SocketException;
/**
- * Attaches a socket filter that accepts ICMP6 router advertisement packets to the given socket.
+ * Attaches a socket filter that accepts ICMPv6 router advertisements to the given socket.
* @param fd the socket's {@link FileDescriptor}.
* @param packetType the hardware address type, one of ARPHRD_*.
*/
public native static void attachRaFilter(FileDescriptor fd, int packetType) throws SocketException;
/**
+ * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
+ * @param fd the socket's {@link FileDescriptor}.
+ * @param ifIndex the interface index.
+ */
+ public native static void setupRaSocket(FileDescriptor fd, int ifIndex) throws SocketException;
+
+ /**
* Binds the current process to the network designated by {@code netId}. All sockets created
* in the future (and not explicitly bound via a bound {@link SocketFactory} (see
* {@link Network#getSocketFactory}) will be bound to this network. Note that if this
diff --git a/core/java/android/net/metrics/ApfProgramEvent.java b/core/java/android/net/metrics/ApfProgramEvent.java
new file mode 100644
index 0000000..258d8e1
--- /dev/null
+++ b/core/java/android/net/metrics/ApfProgramEvent.java
@@ -0,0 +1,135 @@
+/*
+ * 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.net.metrics;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.SparseArray;
+
+import com.android.internal.util.MessageUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.List;
+
+/**
+ * An event logged when there is a change or event that requires updating the
+ * the APF program in place with a new APF program.
+ * {@hide}
+ */
+@SystemApi
+public final class ApfProgramEvent implements Parcelable {
+
+ // Bitflag constants describing what an Apf program filters.
+ // Bits are indexeds from LSB to MSB, starting at index 0.
+ public static final int FLAG_MULTICAST_FILTER_ON = 0;
+ public static final int FLAG_HAS_IPV4_ADDRESS = 1;
+
+ /** {@hide} */
+ @IntDef(flag = true, value = {FLAG_MULTICAST_FILTER_ON, FLAG_HAS_IPV4_ADDRESS})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Flags {}
+
+ public final long lifetime; // Lifetime of the program in seconds
+ public final int filteredRas; // Number of RAs filtered by the APF program
+ public final int currentRas; // Total number of current RAs at generation time
+ public final int programLength; // Length of the APF program in bytes
+ public final int flags; // Bitfield compound of FLAG_* constants
+
+ /** {@hide} */
+ public ApfProgramEvent(
+ long lifetime, int filteredRas, int currentRas, int programLength, @Flags int flags) {
+ this.lifetime = lifetime;
+ this.filteredRas = filteredRas;
+ this.currentRas = currentRas;
+ this.programLength = programLength;
+ this.flags = flags;
+ }
+
+ private ApfProgramEvent(Parcel in) {
+ this.lifetime = in.readLong();
+ this.filteredRas = in.readInt();
+ this.currentRas = in.readInt();
+ this.programLength = in.readInt();
+ this.flags = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(lifetime);
+ out.writeInt(filteredRas);
+ out.writeInt(currentRas);
+ out.writeInt(programLength);
+ out.writeInt(flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ String lifetimeString = (lifetime < Long.MAX_VALUE) ? lifetime + "s" : "forever";
+ return String.format("ApfProgramEvent(%d/%d RAs %dB %s %s)",
+ filteredRas, currentRas, programLength, lifetimeString, namesOf(flags));
+ }
+
+ public static final Parcelable.Creator<ApfProgramEvent> CREATOR
+ = new Parcelable.Creator<ApfProgramEvent>() {
+ public ApfProgramEvent createFromParcel(Parcel in) {
+ return new ApfProgramEvent(in);
+ }
+
+ public ApfProgramEvent[] newArray(int size) {
+ return new ApfProgramEvent[size];
+ }
+ };
+
+ /** {@hide} */
+ public static @Flags int flagsFor(boolean hasIPv4, boolean multicastFilterOn) {
+ int bitfield = 0;
+ if (hasIPv4) {
+ bitfield |= (1 << FLAG_HAS_IPV4_ADDRESS);
+ }
+ if (multicastFilterOn) {
+ bitfield |= (1 << FLAG_MULTICAST_FILTER_ON);
+ }
+ return bitfield;
+ }
+
+ private static String namesOf(@Flags int bitfield) {
+ List<String> names = new ArrayList<>(Integer.bitCount(bitfield));
+ BitSet set = BitSet.valueOf(new long[]{bitfield & Integer.MAX_VALUE});
+ // Only iterate over flag bits which are set.
+ for (int bit = set.nextSetBit(0); bit >= 0; bit = set.nextSetBit(bit+1)) {
+ names.add(Decoder.constants.get(bit));
+ }
+ return TextUtils.join("|", names);
+ }
+
+ final static class Decoder {
+ static final SparseArray<String> constants =
+ MessageUtils.findMessageNames(
+ new Class[]{ApfProgramEvent.class}, new String[]{"FLAG_"});
+ }
+}
diff --git a/core/java/android/net/metrics/ApfStats.java b/core/java/android/net/metrics/ApfStats.java
new file mode 100644
index 0000000..8451e53
--- /dev/null
+++ b/core/java/android/net/metrics/ApfStats.java
@@ -0,0 +1,103 @@
+/*
+ * 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.net.metrics;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * An event logged for an interface with APF capabilities when its IpManager state machine exits.
+ * {@hide}
+ */
+@SystemApi
+public final class ApfStats implements Parcelable {
+
+ public final long durationMs; // time interval in milliseconds these stastistics covers
+ public final int receivedRas; // number of received RAs
+ public final int matchingRas; // number of received RAs matching a known RA
+ public final int droppedRas; // number of received RAs ignored due to the MAX_RAS limit
+ public final int zeroLifetimeRas; // number of received RAs with a minimum lifetime of 0
+ public final int parseErrors; // number of received RAs that could not be parsed
+ public final int programUpdates; // number of APF program updates
+ public final int maxProgramSize; // maximum APF program size advertised by hardware
+
+ /** {@hide} */
+ public ApfStats(long durationMs, int receivedRas, int matchingRas, int droppedRas,
+ int zeroLifetimeRas, int parseErrors, int programUpdates, int maxProgramSize) {
+ this.durationMs = durationMs;
+ this.receivedRas = receivedRas;
+ this.matchingRas = matchingRas;
+ this.droppedRas = droppedRas;
+ this.zeroLifetimeRas = zeroLifetimeRas;
+ this.parseErrors = parseErrors;
+ this.programUpdates = programUpdates;
+ this.maxProgramSize = maxProgramSize;
+ }
+
+ private ApfStats(Parcel in) {
+ this.durationMs = in.readLong();
+ this.receivedRas = in.readInt();
+ this.matchingRas = in.readInt();
+ this.droppedRas = in.readInt();
+ this.zeroLifetimeRas = in.readInt();
+ this.parseErrors = in.readInt();
+ this.programUpdates = in.readInt();
+ this.maxProgramSize = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(durationMs);
+ out.writeInt(receivedRas);
+ out.writeInt(matchingRas);
+ out.writeInt(droppedRas);
+ out.writeInt(zeroLifetimeRas);
+ out.writeInt(parseErrors);
+ out.writeInt(programUpdates);
+ out.writeInt(maxProgramSize);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder("ApfStats(")
+ .append(String.format("%dms ", durationMs))
+ .append(String.format("%dB RA: {", maxProgramSize))
+ .append(String.format("%d received, ", receivedRas))
+ .append(String.format("%d matching, ", matchingRas))
+ .append(String.format("%d dropped, ", droppedRas))
+ .append(String.format("%d zero lifetime, ", zeroLifetimeRas))
+ .append(String.format("%d parse errors, ", parseErrors))
+ .append(String.format("%d program updates})", programUpdates))
+ .toString();
+ }
+
+ public static final Parcelable.Creator<ApfStats> CREATOR = new Parcelable.Creator<ApfStats>() {
+ public ApfStats createFromParcel(Parcel in) {
+ return new ApfStats(in);
+ }
+
+ public ApfStats[] newArray(int size) {
+ return new ApfStats[size];
+ }
+ };
+}
diff --git a/core/java/android/net/metrics/DefaultNetworkEvent.java b/core/java/android/net/metrics/DefaultNetworkEvent.java
index f8b5992..9f0bad7 100644
--- a/core/java/android/net/metrics/DefaultNetworkEvent.java
+++ b/core/java/android/net/metrics/DefaultNetworkEvent.java
@@ -22,10 +22,11 @@
import android.os.Parcelable;
/**
+ * An event recorded by ConnectivityService when there is a change in the default network.
* {@hide}
*/
@SystemApi
-public final class DefaultNetworkEvent extends IpConnectivityEvent implements Parcelable {
+public final class DefaultNetworkEvent implements Parcelable {
// The ID of the network that has become the new default or NETID_UNSET if none.
public final int netId;
// The list of transport types of the new default network, for example TRANSPORT_WIFI, as
@@ -37,7 +38,8 @@
public final boolean prevIPv4;
public final boolean prevIPv6;
- private DefaultNetworkEvent(int netId, int[] transportTypes,
+ /** {@hide} */
+ public DefaultNetworkEvent(int netId, int[] transportTypes,
int prevNetId, boolean prevIPv4, boolean prevIPv6) {
this.netId = netId;
this.transportTypes = transportTypes;
@@ -54,6 +56,7 @@
this.prevIPv6 = (in.readByte() > 0);
}
+ @Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(netId);
out.writeIntArray(transportTypes);
@@ -62,6 +65,7 @@
out.writeByte(prevIPv6 ? (byte) 1 : (byte) 0);
}
+ @Override
public int describeContents() {
return 0;
}
@@ -105,6 +109,5 @@
public static void logEvent(
int netId, int[] transports, int prevNetId, boolean hadIPv4, boolean hadIPv6) {
- logEvent(new DefaultNetworkEvent(netId, transports, prevNetId, hadIPv4, hadIPv6));
}
-};
+}
diff --git a/core/java/android/net/metrics/DhcpClientEvent.java b/core/java/android/net/metrics/DhcpClientEvent.java
index ec560bf..4a9ff05 100644
--- a/core/java/android/net/metrics/DhcpClientEvent.java
+++ b/core/java/android/net/metrics/DhcpClientEvent.java
@@ -21,35 +21,50 @@
import android.os.Parcelable;
/**
+ * An event recorded when a DhcpClient state machine transitions to a new state.
* {@hide}
*/
@SystemApi
-public final class DhcpClientEvent extends IpConnectivityEvent implements Parcelable {
+public final class DhcpClientEvent implements Parcelable {
+
+ // Names for recording DhcpClient pseudo-state transitions.
+ /** {@hide} Represents transitions from DhcpInitState to DhcpBoundState */
+ public static final String INITIAL_BOUND = "InitialBoundState";
+ /** {@hide} Represents transitions from and to DhcpBoundState via DhcpRenewingState */
+ public static final String RENEWING_BOUND = "RenewingBoundState";
+
public final String ifName;
public final String msg;
+ public final int durationMs;
- private DhcpClientEvent(String ifName, String msg) {
+ /** {@hide} */
+ public DhcpClientEvent(String ifName, String msg, int durationMs) {
this.ifName = ifName;
this.msg = msg;
+ this.durationMs = durationMs;
}
private DhcpClientEvent(Parcel in) {
this.ifName = in.readString();
this.msg = in.readString();
+ this.durationMs = in.readInt();
}
+ @Override
public void writeToParcel(Parcel out, int flags) {
out.writeString(ifName);
out.writeString(msg);
+ out.writeInt(durationMs);
}
+ @Override
public int describeContents() {
return 0;
}
@Override
public String toString() {
- return String.format("DhcpClientEvent(%s, %s)", ifName, msg);
+ return String.format("DhcpClientEvent(%s, %s, %dms)", ifName, msg, durationMs);
}
public static final Parcelable.Creator<DhcpClientEvent> CREATOR
@@ -64,6 +79,5 @@
};
public static void logStateEvent(String ifName, String state) {
- logEvent(new DhcpClientEvent(ifName, state));
}
-};
+}
diff --git a/core/java/android/net/metrics/DhcpErrorEvent.java b/core/java/android/net/metrics/DhcpErrorEvent.java
index 84795b8..59c5fb6 100644
--- a/core/java/android/net/metrics/DhcpErrorEvent.java
+++ b/core/java/android/net/metrics/DhcpErrorEvent.java
@@ -24,10 +24,11 @@
import com.android.internal.util.MessageUtils;
/**
- * {@hide} Event class used to record error events when parsing DHCP response packets.
+ * Event class used to record error events when parsing DHCP response packets.
+ * {@hide}
*/
@SystemApi
-public final class DhcpErrorEvent extends IpConnectivityEvent implements Parcelable {
+public final class DhcpErrorEvent implements Parcelable {
public static final int L2_ERROR = 1;
public static final int L3_ERROR = 2;
public static final int L4_ERROR = 3;
@@ -61,7 +62,8 @@
// byte 3: optional code
public final int errorCode;
- private DhcpErrorEvent(String ifName, int errorCode) {
+ /** {@hide} */
+ public DhcpErrorEvent(String ifName, int errorCode) {
this.ifName = ifName;
this.errorCode = errorCode;
}
@@ -71,11 +73,13 @@
this.errorCode = in.readInt();
}
+ @Override
public void writeToParcel(Parcel out, int flags) {
out.writeString(ifName);
out.writeInt(errorCode);
}
+ @Override
public int describeContents() {
return 0;
}
@@ -92,11 +96,9 @@
};
public static void logParseError(String ifName, int errorCode) {
- logEvent(new DhcpErrorEvent(ifName, errorCode));
}
public static void logReceiveError(String ifName) {
- logEvent(new DhcpErrorEvent(ifName, RECEIVE_ERROR));
}
public static int errorCodeWithOption(int errorCode, int option) {
diff --git a/core/java/android/net/metrics/DnsEvent.java b/core/java/android/net/metrics/DnsEvent.java
index b94dda0..4fc6b7a 100644
--- a/core/java/android/net/metrics/DnsEvent.java
+++ b/core/java/android/net/metrics/DnsEvent.java
@@ -21,10 +21,11 @@
import android.os.Parcelable;
/**
+ * An event recorded by DnsEventListenerService.
* {@hide}
*/
@SystemApi
-final public class DnsEvent extends IpConnectivityEvent implements Parcelable {
+final public class DnsEvent implements Parcelable {
public final int netId;
// The event type is currently only 1 or 2, so we store it as a byte.
@@ -37,7 +38,8 @@
// queries.
public final int[] latenciesMs;
- private DnsEvent(int netId, byte[] eventTypes, byte[] returnCodes, int[] latenciesMs) {
+ /** {@hide} */
+ public DnsEvent(int netId, byte[] eventTypes, byte[] returnCodes, int[] latenciesMs) {
this.netId = netId;
this.eventTypes = eventTypes;
this.returnCodes = returnCodes;
@@ -59,6 +61,7 @@
out.writeIntArray(latenciesMs);
}
+ @Override
public int describeContents() {
return 0;
}
@@ -82,6 +85,5 @@
public static void logEvent(
int netId, byte[] eventTypes, byte[] returnCodes, int[] latenciesMs) {
- logEvent(new DnsEvent(netId, eventTypes, returnCodes, latenciesMs));
}
}
diff --git a/core/java/android/net/metrics/IpConnectivityEvent.java b/core/java/android/net/metrics/IpConnectivityEvent.java
deleted file mode 100644
index 95576c2..0000000
--- a/core/java/android/net/metrics/IpConnectivityEvent.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.metrics;
-
-import android.net.ConnectivityMetricsLogger;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * {@hide}
- */
-public abstract class IpConnectivityEvent {
- private static final int COMPONENT_TAG = ConnectivityMetricsLogger.COMPONENT_TAG_CONNECTIVITY;
-
- private static final ConnectivityMetricsLogger sMetricsLogger = new ConnectivityMetricsLogger();
-
- public static <T extends IpConnectivityEvent & Parcelable> void logEvent(T event) {
- // TODO: consider using different component for DNS event.
- sMetricsLogger.logEvent(System.currentTimeMillis(), COMPONENT_TAG, 0, event);
- }
-};
diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java
new file mode 100644
index 0000000..dd7bd1b
--- /dev/null
+++ b/core/java/android/net/metrics/IpConnectivityLog.java
@@ -0,0 +1,86 @@
+/*
+ * 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.net.metrics;
+
+import android.net.ConnectivityMetricsEvent;
+import android.net.ConnectivityMetricsLogger;
+import android.net.IConnectivityMetricsLogger;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Specialization of the ConnectivityMetricsLogger class for recording IP connectivity events.
+ * {@hide}
+ */
+public class IpConnectivityLog extends ConnectivityMetricsLogger {
+ private static String TAG = "IpConnectivityMetricsLogger";
+ private static final boolean DBG = true;
+
+ public IpConnectivityLog() {
+ // mService initialized in super constructor.
+ }
+
+ @VisibleForTesting
+ public IpConnectivityLog(IConnectivityMetricsLogger service) {
+ super(service);
+ }
+
+ /**
+ * Log an IpConnectivity event. Contrary to logEvent(), this method does not
+ * keep track of skipped events and is thread-safe for callers.
+ *
+ * @param timestamp is the epoch timestamp of the event in ms.
+ * @param data is a Parcelable instance representing the event.
+ *
+ * @return true if the event was successfully logged.
+ */
+ public boolean log(long timestamp, Parcelable data) {
+ if (!checkLoggerService()) {
+ if (DBG) {
+ Log.d(TAG, CONNECTIVITY_METRICS_LOGGER_SERVICE + " service was not ready");
+ }
+ return false;
+ }
+
+ if (System.currentTimeMillis() < mServiceUnblockedTimestampMillis) {
+ if (DBG) {
+ Log.d(TAG, "skipping logging due to throttling for IpConnectivity component");
+ }
+ return false;
+ }
+
+ try {
+ final ConnectivityMetricsEvent event =
+ new ConnectivityMetricsEvent(timestamp, COMPONENT_TAG_CONNECTIVITY, 0, data);
+ final long result = mService.logEvent(event);
+ if (result >= 0) {
+ mServiceUnblockedTimestampMillis = result;
+ }
+ return (result == 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error logging event", e);
+ return false;
+ }
+ }
+
+ public void log(Parcelable event) {
+ log(System.currentTimeMillis(), event);
+ }
+}
diff --git a/core/java/android/net/metrics/IpManagerEvent.java b/core/java/android/net/metrics/IpManagerEvent.java
index 0940bd0..a5b4eb5 100644
--- a/core/java/android/net/metrics/IpManagerEvent.java
+++ b/core/java/android/net/metrics/IpManagerEvent.java
@@ -16,6 +16,7 @@
package android.net.metrics;
+import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -23,21 +24,32 @@
import com.android.internal.util.MessageUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
+ * An event recorded by IpManager when IP provisioning completes for a network or
+ * when a network disconnects.
* {@hide}
*/
@SystemApi
-public final class IpManagerEvent extends IpConnectivityEvent implements Parcelable {
+public final class IpManagerEvent implements Parcelable {
public static final int PROVISIONING_OK = 1;
public static final int PROVISIONING_FAIL = 2;
public static final int COMPLETE_LIFECYCLE = 3;
+ /** {@hide} */
+ @IntDef(value = {PROVISIONING_OK, PROVISIONING_FAIL, COMPLETE_LIFECYCLE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EventType {}
+
public final String ifName;
- public final int eventType;
+ public final @EventType int eventType;
public final long durationMs;
- private IpManagerEvent(String ifName, int eventType, long duration) {
+ /** {@hide} */
+ public IpManagerEvent(String ifName, @EventType int eventType, long duration) {
this.ifName = ifName;
this.eventType = eventType;
this.durationMs = duration;
@@ -49,12 +61,14 @@
this.durationMs = in.readLong();
}
+ @Override
public void writeToParcel(Parcel out, int flags) {
out.writeString(ifName);
out.writeInt(eventType);
out.writeLong(durationMs);
}
+ @Override
public int describeContents() {
return 0;
}
@@ -71,7 +85,6 @@
};
public static void logEvent(int eventType, String ifName, long durationMs) {
- logEvent(new IpManagerEvent(ifName, eventType, durationMs));
}
@Override
@@ -84,4 +97,4 @@
static final SparseArray<String> constants = MessageUtils.findMessageNames(
new Class[]{IpManagerEvent.class}, new String[]{"PROVISIONING_", "COMPLETE_"});
}
-};
+}
diff --git a/core/java/android/net/metrics/IpReachabilityEvent.java b/core/java/android/net/metrics/IpReachabilityEvent.java
index d40389c..ee09e22 100644
--- a/core/java/android/net/metrics/IpReachabilityEvent.java
+++ b/core/java/android/net/metrics/IpReachabilityEvent.java
@@ -24,24 +24,35 @@
import com.android.internal.util.MessageUtils;
/**
+ * An event recorded when IpReachabilityMonitor sends a neighbor probe or receives
+ * a neighbor probe result.
* {@hide}
*/
@SystemApi
-public final class IpReachabilityEvent extends IpConnectivityEvent implements Parcelable {
+public final class IpReachabilityEvent implements Parcelable {
- public static final int PROBE = 1 << 8;
- public static final int NUD_FAILED = 2 << 8;
- public static final int PROVISIONING_LOST = 3 << 8;
+ // Event types.
+ /** A probe forced by IpReachabilityMonitor. */
+ public static final int PROBE = 1 << 8;
+ /** Neighbor unreachable after a forced probe. */
+ public static final int NUD_FAILED = 2 << 8;
+ /** Neighbor unreachable after a forced probe, IP provisioning is also lost. */
+ public static final int PROVISIONING_LOST = 3 << 8;
+ /** {@hide} Neighbor unreachable notification from kernel. */
+ public static final int NUD_FAILED_ORGANIC = 4 << 8;
+ /** {@hide} Neighbor unreachable notification from kernel, IP provisioning is also lost. */
+ public static final int PROVISIONING_LOST_ORGANIC = 5 << 8;
public final String ifName;
// eventType byte format (MSB to LSB):
// byte 0: unused
// byte 1: unused
// byte 2: type of event: PROBE, NUD_FAILED, PROVISIONING_LOST
- // byte 3: kernel errno from RTNetlink or IpReachabilityMonitor
+ // byte 3: when byte 2 == PROBE, errno code from RTNetlink or IpReachabilityMonitor.
public final int eventType;
- private IpReachabilityEvent(String ifName, int eventType) {
+ /** {@hide} */
+ public IpReachabilityEvent(String ifName, int eventType) {
this.ifName = ifName;
this.eventType = eventType;
}
@@ -51,11 +62,13 @@
this.eventType = in.readInt();
}
+ @Override
public void writeToParcel(Parcel out, int flags) {
out.writeString(ifName);
out.writeInt(eventType);
}
+ @Override
public int describeContents() {
return 0;
}
@@ -72,21 +85,32 @@
};
public static void logProbeEvent(String ifName, int nlErrorCode) {
- logEvent(new IpReachabilityEvent(ifName, PROBE | (nlErrorCode & 0xFF)));
}
public static void logNudFailed(String ifName) {
- logEvent(new IpReachabilityEvent(ifName, NUD_FAILED));
}
public static void logProvisioningLost(String ifName) {
- logEvent(new IpReachabilityEvent(ifName, PROVISIONING_LOST));
+ }
+
+ /**
+ * Returns the NUD failure event type code corresponding to the given conditions.
+ * {@hide}
+ */
+ public static int nudFailureEventType(boolean isFromProbe, boolean isProvisioningLost) {
+ if (isFromProbe) {
+ return isProvisioningLost ? PROVISIONING_LOST : NUD_FAILED;
+ } else {
+ return isProvisioningLost ? PROVISIONING_LOST_ORGANIC : NUD_FAILED_ORGANIC;
+ }
}
@Override
public String toString() {
- return String.format("IpReachabilityEvent(%s, %s)", ifName,
- Decoder.constants.get(eventType));
+ int hi = eventType & 0xff00;
+ int lo = eventType & 0x00ff;
+ String eventName = Decoder.constants.get(hi);
+ return String.format("IpReachabilityEvent(%s, %s:%02x)", ifName, eventName, lo);
}
final static class Decoder {
@@ -94,4 +118,4 @@
MessageUtils.findMessageNames(new Class[]{IpReachabilityEvent.class},
new String[]{"PROBE", "PROVISIONING_", "NUD_"});
}
-};
+}
diff --git a/core/java/android/net/metrics/NetworkEvent.java b/core/java/android/net/metrics/NetworkEvent.java
index 08c9c75..3b3fa69 100644
--- a/core/java/android/net/metrics/NetworkEvent.java
+++ b/core/java/android/net/metrics/NetworkEvent.java
@@ -16,6 +16,7 @@
package android.net.metrics;
+import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -23,11 +24,14 @@
import com.android.internal.util.MessageUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* {@hide}
*/
@SystemApi
-public final class NetworkEvent extends IpConnectivityEvent implements Parcelable {
+public final class NetworkEvent implements Parcelable {
public static final int NETWORK_CONNECTED = 1;
public static final int NETWORK_VALIDATED = 2;
@@ -37,28 +41,49 @@
public static final int NETWORK_UNLINGER = 6;
public static final int NETWORK_DISCONNECTED = 7;
+ /** {@hide} */
+ @IntDef(value = {
+ NETWORK_CONNECTED,
+ NETWORK_VALIDATED,
+ NETWORK_VALIDATION_FAILED,
+ NETWORK_CAPTIVE_PORTAL_FOUND,
+ NETWORK_LINGER,
+ NETWORK_UNLINGER,
+ NETWORK_DISCONNECTED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EventType {}
+
public final int netId;
- public final int eventType;
+ public final @EventType int eventType;
public final long durationMs;
- private NetworkEvent(int netId, int eventType, long durationMs) {
+ /** {@hide} */
+ public NetworkEvent(int netId, @EventType int eventType, long durationMs) {
this.netId = netId;
this.eventType = eventType;
this.durationMs = durationMs;
}
+ /** {@hide} */
+ public NetworkEvent(int netId, @EventType int eventType) {
+ this(netId, eventType, 0);
+ }
+
private NetworkEvent(Parcel in) {
netId = in.readInt();
eventType = in.readInt();
durationMs = in.readLong();
}
+ @Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(netId);
out.writeInt(eventType);
out.writeLong(durationMs);
}
+ @Override
public int describeContents() {
return 0;
}
@@ -75,15 +100,12 @@
};
public static void logEvent(int netId, int eventType) {
- logEvent(new NetworkEvent(netId, eventType, 0));
}
public static void logValidated(int netId, long durationMs) {
- logEvent(new NetworkEvent(netId, NETWORK_VALIDATED, durationMs));
}
public static void logCaptivePortalFound(int netId, long durationMs) {
- logEvent(new NetworkEvent(netId, NETWORK_CAPTIVE_PORTAL_FOUND, durationMs));
}
@Override
@@ -96,4 +118,4 @@
static final SparseArray<String> constants = MessageUtils.findMessageNames(
new Class[]{NetworkEvent.class}, new String[]{"NETWORK_"});
}
-};
+}
diff --git a/core/java/android/net/metrics/RaEvent.java b/core/java/android/net/metrics/RaEvent.java
new file mode 100644
index 0000000..91bd023
--- /dev/null
+++ b/core/java/android/net/metrics/RaEvent.java
@@ -0,0 +1,154 @@
+/*
+ * 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.net.metrics;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * An event logged when the APF packet socket receives an RA packet.
+ * {@hide}
+ */
+@SystemApi
+public final class RaEvent implements Parcelable {
+
+ /** {@hide} */
+ public static final long NO_LIFETIME = -1L;
+
+ // Lifetime in seconds of options found in a single RA packet.
+ // When an option is not set, the value of the associated field is -1;
+ public final long routerLifetime;
+ public final long prefixValidLifetime;
+ public final long prefixPreferredLifetime;
+ public final long routeInfoLifetime;
+ public final long rdnssLifetime;
+ public final long dnsslLifetime;
+
+ /** {@hide} */
+ public RaEvent(long routerLifetime, long prefixValidLifetime, long prefixPreferredLifetime,
+ long routeInfoLifetime, long rdnssLifetime, long dnsslLifetime) {
+ this.routerLifetime = routerLifetime;
+ this.prefixValidLifetime = prefixValidLifetime;
+ this.prefixPreferredLifetime = prefixPreferredLifetime;
+ this.routeInfoLifetime = routeInfoLifetime;
+ this.rdnssLifetime = rdnssLifetime;
+ this.dnsslLifetime = dnsslLifetime;
+ }
+
+ private RaEvent(Parcel in) {
+ routerLifetime = in.readLong();
+ prefixValidLifetime = in.readLong();
+ prefixPreferredLifetime = in.readLong();
+ routeInfoLifetime = in.readLong();
+ rdnssLifetime = in.readLong();
+ dnsslLifetime = in.readLong();
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(routerLifetime);
+ out.writeLong(prefixValidLifetime);
+ out.writeLong(prefixPreferredLifetime);
+ out.writeLong(routeInfoLifetime);
+ out.writeLong(rdnssLifetime);
+ out.writeLong(dnsslLifetime);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder("RaEvent(lifetimes: ")
+ .append(String.format("router=%ds, ", routerLifetime))
+ .append(String.format("prefix_valid=%ds, ", prefixValidLifetime))
+ .append(String.format("prefix_preferred=%ds, ", prefixPreferredLifetime))
+ .append(String.format("route_info=%ds, ", routeInfoLifetime))
+ .append(String.format("rdnss=%ds, ", rdnssLifetime))
+ .append(String.format("dnssl=%ds)", dnsslLifetime))
+ .toString();
+ }
+
+ public static final Parcelable.Creator<RaEvent> CREATOR = new Parcelable.Creator<RaEvent>() {
+ public RaEvent createFromParcel(Parcel in) {
+ return new RaEvent(in);
+ }
+
+ public RaEvent[] newArray(int size) {
+ return new RaEvent[size];
+ }
+ };
+
+ /** {@hide} */
+ public static class Builder {
+
+ long routerLifetime = NO_LIFETIME;
+ long prefixValidLifetime = NO_LIFETIME;
+ long prefixPreferredLifetime = NO_LIFETIME;
+ long routeInfoLifetime = NO_LIFETIME;
+ long rdnssLifetime = NO_LIFETIME;
+ long dnsslLifetime = NO_LIFETIME;
+
+ public Builder() {
+ }
+
+ public RaEvent build() {
+ return new RaEvent(routerLifetime, prefixValidLifetime, prefixPreferredLifetime,
+ routeInfoLifetime, rdnssLifetime, dnsslLifetime);
+ }
+
+ public Builder updateRouterLifetime(long lifetime) {
+ routerLifetime = updateLifetime(routerLifetime, lifetime);
+ return this;
+ }
+
+ public Builder updatePrefixValidLifetime(long lifetime) {
+ prefixValidLifetime = updateLifetime(prefixValidLifetime, lifetime);
+ return this;
+ }
+
+ public Builder updatePrefixPreferredLifetime(long lifetime) {
+ prefixPreferredLifetime = updateLifetime(prefixPreferredLifetime, lifetime);
+ return this;
+ }
+
+ public Builder updateRouteInfoLifetime(long lifetime) {
+ routeInfoLifetime = updateLifetime(routeInfoLifetime, lifetime);
+ return this;
+ }
+
+ public Builder updateRdnssLifetime(long lifetime) {
+ rdnssLifetime = updateLifetime(rdnssLifetime, lifetime);
+ return this;
+ }
+
+ public Builder updateDnsslLifetime(long lifetime) {
+ dnsslLifetime = updateLifetime(dnsslLifetime, lifetime);
+ return this;
+ }
+
+ private long updateLifetime(long currentLifetime, long newLifetime) {
+ if (currentLifetime == RaEvent.NO_LIFETIME) {
+ return newLifetime;
+ }
+ return Math.min(currentLifetime, newLifetime);
+ }
+ }
+}
diff --git a/core/java/android/net/metrics/ValidationProbeEvent.java b/core/java/android/net/metrics/ValidationProbeEvent.java
index 751c35f..331cf0c 100644
--- a/core/java/android/net/metrics/ValidationProbeEvent.java
+++ b/core/java/android/net/metrics/ValidationProbeEvent.java
@@ -16,6 +16,7 @@
package android.net.metrics;
+import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -23,11 +24,15 @@
import com.android.internal.util.MessageUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
+ * An event recorded by NetworkMonitor when sending a probe for finding captive portals.
* {@hide}
*/
@SystemApi
-public final class ValidationProbeEvent extends IpConnectivityEvent implements Parcelable {
+public final class ValidationProbeEvent implements Parcelable {
public static final int PROBE_DNS = 0;
public static final int PROBE_HTTP = 1;
@@ -37,12 +42,24 @@
public static final int DNS_FAILURE = 0;
public static final int DNS_SUCCESS = 1;
+ /** {@hide} */
+ @IntDef(value = {PROBE_DNS, PROBE_HTTP, PROBE_HTTPS, PROBE_PAC})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProbeType {}
+
+ /** {@hide} */
+ @IntDef(value = {DNS_FAILURE, DNS_SUCCESS})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ReturnCode {}
+
public final int netId;
public final long durationMs;
- public final int probeType;
- public final int returnCode;
+ public final @ProbeType int probeType;
+ public final @ReturnCode int returnCode;
- private ValidationProbeEvent(int netId, long durationMs, int probeType, int returnCode) {
+ /** @hide */
+ public ValidationProbeEvent(
+ int netId, long durationMs, @ProbeType int probeType, @ReturnCode int returnCode) {
this.netId = netId;
this.durationMs = durationMs;
this.probeType = probeType;
@@ -56,6 +73,7 @@
returnCode = in.readInt();
}
+ @Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(netId);
out.writeLong(durationMs);
@@ -63,6 +81,7 @@
out.writeInt(returnCode);
}
+ @Override
public int describeContents() {
return 0;
}
@@ -84,7 +103,6 @@
}
public static void logEvent(int netId, long durationMs, int probeType, int returnCode) {
- logEvent(new ValidationProbeEvent(netId, durationMs, probeType, returnCode));
}
@Override
@@ -97,4 +115,4 @@
static final SparseArray<String> constants = MessageUtils.findMessageNames(
new Class[]{ValidationProbeEvent.class}, new String[]{"PROBE_"});
}
-};
+}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 41ff9fd..783c25a 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -290,6 +290,7 @@
// Guarded by NfcAdapter.class
static boolean sIsInitialized = false;
+ static boolean sHasNfcFeature;
// Final after first constructor, except for
// attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
@@ -435,42 +436,65 @@
}
/**
+ * Helper to check if this device is NFC HCE capable, by checking for
+ * FEATURE_NFC_HOST_CARD_EMULATION and/or FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+ * but without using a context.
+ */
+ private static boolean hasNfcHceFeature() {
+ IPackageManager pm = ActivityThread.getPackageManager();
+ if (pm == null) {
+ Log.e(TAG, "Cannot get package manager, assuming no NFC feature");
+ return false;
+ }
+ try {
+ return pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION, 0)
+ || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF, 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Package manager query failed, assuming no NFC feature", e);
+ return false;
+ }
+ }
+
+ /**
* Returns the NfcAdapter for application context,
* or throws if NFC is not available.
* @hide
*/
public static synchronized NfcAdapter getNfcAdapter(Context context) {
if (!sIsInitialized) {
+ sHasNfcFeature = hasNfcFeature();
+ boolean hasHceFeature = hasNfcHceFeature();
/* is this device meant to have NFC */
- if (!hasNfcFeature()) {
+ if (!sHasNfcFeature && !hasHceFeature) {
Log.v(TAG, "this device does not have NFC support");
throw new UnsupportedOperationException();
}
-
sService = getServiceInterface();
if (sService == null) {
Log.e(TAG, "could not retrieve NFC service");
throw new UnsupportedOperationException();
}
- try {
- sTagService = sService.getNfcTagInterface();
- } catch (RemoteException e) {
- Log.e(TAG, "could not retrieve NFC Tag service");
- throw new UnsupportedOperationException();
+ if (sHasNfcFeature) {
+ try {
+ sTagService = sService.getNfcTagInterface();
+ } catch (RemoteException e) {
+ Log.e(TAG, "could not retrieve NFC Tag service");
+ throw new UnsupportedOperationException();
+ }
}
-
- try {
- sCardEmulationService = sService.getNfcCardEmulationInterface();
- } catch (RemoteException e) {
- Log.e(TAG, "could not retrieve card emulation service");
- throw new UnsupportedOperationException();
- }
-
- try {
- sNfcFCardEmulationService = sService.getNfcFCardEmulationInterface();
- } catch (RemoteException e) {
- Log.e(TAG, "could not retrieve NFC-F card emulation service");
- throw new UnsupportedOperationException();
+ if (hasHceFeature) {
+ try {
+ sNfcFCardEmulationService = sService.getNfcFCardEmulationInterface();
+ } catch (RemoteException e) {
+ Log.e(TAG, "could not retrieve NFC-F card emulation service");
+ throw new UnsupportedOperationException();
+ }
+ try {
+ sCardEmulationService = sService.getNfcCardEmulationInterface();
+ } catch (RemoteException e) {
+ Log.e(TAG, "could not retrieve card emulation service");
+ throw new UnsupportedOperationException();
+ }
}
sIsInitialized = true;
@@ -838,8 +862,14 @@
*
* @param uris an array of Uri(s) to push over Android Beam
* @param activity activity for which the Uri(s) will be pushed
+ * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
*/
public void setBeamPushUris(Uri[] uris, Activity activity) {
+ synchronized (NfcAdapter.class) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
if (activity == null) {
throw new NullPointerException("activity cannot be null");
}
@@ -914,8 +944,14 @@
*
* @param callback callback, or null to disable
* @param activity activity for which the Uri(s) will be pushed
+ * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
*/
public void setBeamPushUrisCallback(CreateBeamUrisCallback callback, Activity activity) {
+ synchronized (NfcAdapter.class) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
if (activity == null) {
throw new NullPointerException("activity cannot be null");
}
@@ -992,9 +1028,15 @@
* @param activities optional additional activities, however we strongly recommend
* to only register one at a time, and to do so in that activity's
* {@link Activity#onCreate}
+ * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
*/
public void setNdefPushMessage(NdefMessage message, Activity activity,
Activity ... activities) {
+ synchronized (NfcAdapter.class) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
int targetSdkVersion = getSdkVersion();
try {
if (activity == null) {
@@ -1024,6 +1066,11 @@
*/
@SystemApi
public void setNdefPushMessage(NdefMessage message, Activity activity, int flags) {
+ synchronized (NfcAdapter.class) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
if (activity == null) {
throw new NullPointerException("activity cannot be null");
}
@@ -1094,9 +1141,15 @@
* @param activities optional additional activities, however we strongly recommend
* to only register one at a time, and to do so in that activity's
* {@link Activity#onCreate}
+ * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
*/
public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity,
Activity ... activities) {
+ synchronized (NfcAdapter.class) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
int targetSdkVersion = getSdkVersion();
try {
if (activity == null) {
@@ -1168,9 +1221,15 @@
* @param activities optional additional activities, however we strongly recommend
* to only register one at a time, and to do so in that activity's
* {@link Activity#onCreate}
+ * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
*/
public void setOnNdefPushCompleteCallback(OnNdefPushCompleteCallback callback,
Activity activity, Activity ... activities) {
+ synchronized (NfcAdapter.class) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
int targetSdkVersion = getSdkVersion();
try {
if (activity == null) {
@@ -1227,9 +1286,15 @@
* @param techLists the tech lists used to perform matching for dispatching of the
* {@link NfcAdapter#ACTION_TECH_DISCOVERED} intent
* @throws IllegalStateException if the Activity is not currently in the foreground
+ * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
*/
public void enableForegroundDispatch(Activity activity, PendingIntent intent,
IntentFilter[] filters, String[][] techLists) {
+ synchronized (NfcAdapter.class) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
if (activity == null || intent == null) {
throw new NullPointerException();
}
@@ -1263,8 +1328,14 @@
*
* @param activity the Activity to disable dispatch to
* @throws IllegalStateException if the Activity has already been paused
+ * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
*/
public void disableForegroundDispatch(Activity activity) {
+ synchronized (NfcAdapter.class) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
ActivityThread.currentActivityThread().unregisterOnActivityPausedListener(activity,
mForegroundDispatchListener);
disableForegroundDispatchInternal(activity, false);
@@ -1309,9 +1380,15 @@
* @param callback the callback to be called when a tag is discovered
* @param flags Flags indicating poll technologies and other optional parameters
* @param extras Additional extras for configuring reader mode.
+ * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
*/
public void enableReaderMode(Activity activity, ReaderCallback callback, int flags,
Bundle extras) {
+ synchronized (NfcAdapter.class) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
mNfcActivityManager.enableReaderMode(activity, callback, flags, extras);
}
@@ -1321,8 +1398,14 @@
* all supported tag technologies.
*
* @param activity the Activity that currently has reader mode enabled
+ * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
*/
public void disableReaderMode(Activity activity) {
+ synchronized (NfcAdapter.class) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
mNfcActivityManager.disableReaderMode(activity);
}
@@ -1349,8 +1432,14 @@
*
* @param activity the current foreground Activity that has registered data to share
* @return whether the Beam animation was successfully invoked
+ * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
*/
public boolean invokeBeam(Activity activity) {
+ synchronized (NfcAdapter.class) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
if (activity == null) {
throw new NullPointerException("activity may not be null.");
}
@@ -1404,10 +1493,16 @@
* @param activity foreground activity
* @param message a NDEF Message to push over NFC
* @throws IllegalStateException if the activity is not currently in the foreground
+ * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
* @deprecated use {@link #setNdefPushMessage} instead
*/
@Deprecated
public void enableForegroundNdefPush(Activity activity, NdefMessage message) {
+ synchronized (NfcAdapter.class) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
if (activity == null || message == null) {
throw new NullPointerException();
}
@@ -1432,10 +1527,16 @@
*
* @param activity the Foreground activity
* @throws IllegalStateException if the Activity has already been paused
+ * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
* @deprecated use {@link #setNdefPushMessage} instead
*/
@Deprecated
public void disableForegroundNdefPush(Activity activity) {
+ synchronized (NfcAdapter.class) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
if (activity == null) {
throw new NullPointerException();
}
@@ -1452,6 +1553,9 @@
*/
@SystemApi
public boolean enableNdefPush() {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
try {
return sService.enableNdefPush();
} catch (RemoteException e) {
@@ -1467,6 +1571,11 @@
*/
@SystemApi
public boolean disableNdefPush() {
+ synchronized (NfcAdapter.class) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
try {
return sService.disableNdefPush();
} catch (RemoteException e) {
@@ -1497,8 +1606,14 @@
*
* @see android.provider.Settings#ACTION_NFCSHARING_SETTINGS
* @return true if NDEF Push feature is enabled
+ * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
*/
public boolean isNdefPushEnabled() {
+ synchronized (NfcAdapter.class) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
try {
return sService.isNdefPushEnabled();
} catch (RemoteException e) {
@@ -1623,6 +1738,11 @@
@SystemApi
public boolean addNfcUnlockHandler(final NfcUnlockHandler unlockHandler,
String[] tagTechnologies) {
+ synchronized (NfcAdapter.class) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
// If there are no tag technologies, don't bother adding unlock handler
if (tagTechnologies.length == 0) {
return false;
@@ -1666,6 +1786,11 @@
*/
@SystemApi
public boolean removeNfcUnlockHandler(NfcUnlockHandler unlockHandler) {
+ synchronized (NfcAdapter.class) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
try {
synchronized (mLock) {
if (mNfcUnlockHandlers.containsKey(unlockHandler)) {
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 4acb729..a0c2efd 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -30,6 +30,8 @@
import android.telephony.SignalStrength;
import android.text.format.DateFormat;
import android.util.ArrayMap;
+import android.util.Log;
+import android.util.LongSparseArray;
import android.util.MutableBoolean;
import android.util.Pair;
import android.util.Printer;
@@ -47,6 +49,7 @@
* @hide
*/
public abstract class BatteryStats implements Parcelable {
+ private static final String TAG = "BatteryStats";
private static final boolean LOCAL_LOGV = false;
@@ -175,8 +178,11 @@
/**
* Current version of checkin data format.
+ *
+ * New in version 19:
+ * - Wakelock data (wl) gets current and max times.
*/
- static final String CHECKIN_VERSION = "18";
+ static final String CHECKIN_VERSION = "19";
/**
* Old version, we hit 9 and ran out of room, need to remove.
@@ -352,6 +358,32 @@
public abstract long getTimeSinceMarkLocked(long elapsedRealtimeUs);
/**
+ * Returns the max duration if it is being tracked.
+ * Not all Timer subclasses track the max duration and the current duration.
+
+ */
+ public long getMaxDurationMsLocked(long elapsedRealtimeMs) {
+ return -1;
+ }
+
+ /**
+ * Returns the current time the timer has been active, if it is being tracked.
+ * Not all Timer subclasses track the max duration and the current duration.
+ */
+ public long getCurrentDurationMsLocked(long elapsedRealtimeMs) {
+ return -1;
+ }
+
+ /**
+ * Returns whether the timer is currently running. Some types of timers
+ * (e.g. BatchTimers) don't know whether the event is currently active,
+ * and report false.
+ */
+ public boolean isRunningLocked() {
+ return false;
+ }
+
+ /**
* Temporary for debugging.
*/
public abstract void logState(Printer pw, String prefix);
@@ -546,6 +578,20 @@
*/
public abstract long getTimeAtCpuSpeed(int cluster, int step, int which);
+ /**
+ * Returns the number of times this UID woke up the Application Processor to
+ * process a mobile radio packet.
+ * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+ */
+ public abstract long getMobileRadioApWakeupCount(int which);
+
+ /**
+ * Returns the number of times this UID woke up the Application Processor to
+ * process a WiFi packet.
+ * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+ */
+ public abstract long getWifiRadioApWakeupCount(int which);
+
public static abstract class Sensor {
/*
* FIXME: it's not correct to use this magic value because it
@@ -1285,9 +1331,14 @@
public static final int EVENT_TEMP_WHITELIST = 0x0011;
// Event for the screen waking up.
public static final int EVENT_SCREEN_WAKE_UP = 0x0012;
+ // Event for the UID that woke up the application processor.
+ // Used for wakeups coming from WiFi, modem, etc.
+ public static final int EVENT_WAKEUP_AP = 0x0013;
+ // Event for reporting that a specific partial wake lock has been held for a long duration.
+ public static final int EVENT_LONG_WAKE_LOCK = 0x0014;
// Number of event types.
- public static final int EVENT_COUNT = 0x0013;
+ public static final int EVENT_COUNT = 0x0015;
// Mask to extract out only the type part of the event.
public static final int EVENT_TYPE_MASK = ~(EVENT_FLAG_START|EVENT_FLAG_FINISH);
@@ -1315,6 +1366,10 @@
EVENT_TEMP_WHITELIST | EVENT_FLAG_START;
public static final int EVENT_TEMP_WHITELIST_FINISH =
EVENT_TEMP_WHITELIST | EVENT_FLAG_FINISH;
+ public static final int EVENT_LONG_WAKE_LOCK_START =
+ EVENT_LONG_WAKE_LOCK | EVENT_FLAG_START;
+ public static final int EVENT_LONG_WAKE_LOCK_FINISH =
+ EVENT_LONG_WAKE_LOCK | EVENT_FLAG_FINISH;
// For CMD_EVENT.
public int eventCode;
@@ -1979,13 +2034,13 @@
public static final String[] HISTORY_EVENT_NAMES = new String[] {
"null", "proc", "fg", "top", "sync", "wake_lock_in", "job", "user", "userfg", "conn",
"active", "pkginst", "pkgunin", "alarm", "stats", "inactive", "active", "tmpwhitelist",
- "screenwake",
+ "screenwake", "wakeupap", "longwake"
};
public static final String[] HISTORY_EVENT_CHECKIN_NAMES = new String[] {
"Enl", "Epr", "Efg", "Etp", "Esy", "Ewl", "Ejb", "Eur", "Euf", "Ecn",
"Eac", "Epi", "Epu", "Eal", "Est", "Eai", "Eaa", "Etw",
- "Esw",
+ "Esw", "Ewa", "Elw"
};
/**
@@ -2535,6 +2590,22 @@
sb.append('(');
sb.append(count);
sb.append(" times)");
+ final long maxDurationMs = timer.getMaxDurationMsLocked(elapsedRealtimeUs/1000);
+ if (maxDurationMs >= 0) {
+ sb.append(" max=");
+ sb.append(maxDurationMs);
+ }
+ if (timer.isRunningLocked()) {
+ final long currentMs = timer.getCurrentDurationMsLocked(elapsedRealtimeUs/1000);
+ if (currentMs >= 0) {
+ sb.append(" (running for ");
+ sb.append(currentMs);
+ sb.append("ms)");
+ } else {
+ sb.append(" (running)");
+ }
+ }
+
return ", ";
}
}
@@ -2542,6 +2613,7 @@
}
/**
+ * Prints details about a timer, if its total time was greater than 0.
*
* @param pw a PrintWriter object to print to.
* @param sb a StringBuilder object.
@@ -2550,24 +2622,40 @@
* @param which which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
* @param prefix a String to be prepended to each line of output.
* @param type the name of the timer.
+ * @return true if anything was printed.
*/
private static final boolean printTimer(PrintWriter pw, StringBuilder sb, Timer timer,
- long rawRealtime, int which, String prefix, String type) {
+ long rawRealtimeUs, int which, String prefix, String type) {
if (timer != null) {
// Convert from microseconds to milliseconds with rounding
- final long totalTime = (timer.getTotalTimeLocked(
- rawRealtime, which) + 500) / 1000;
+ final long totalTimeMs = (timer.getTotalTimeLocked(
+ rawRealtimeUs, which) + 500) / 1000;
final int count = timer.getCountLocked(which);
- if (totalTime != 0) {
+ if (totalTimeMs != 0) {
sb.setLength(0);
sb.append(prefix);
sb.append(" ");
sb.append(type);
sb.append(": ");
- formatTimeMs(sb, totalTime);
+ formatTimeMs(sb, totalTimeMs);
sb.append("realtime (");
sb.append(count);
sb.append(" times)");
+ final long maxDurationMs = timer.getMaxDurationMsLocked(rawRealtimeUs/1000);
+ if (maxDurationMs >= 0) {
+ sb.append(" max=");
+ sb.append(maxDurationMs);
+ }
+ if (timer.isRunningLocked()) {
+ final long currentMs = timer.getCurrentDurationMsLocked(rawRealtimeUs/1000);
+ if (currentMs >= 0) {
+ sb.append(" (running for ");
+ sb.append(currentMs);
+ sb.append("ms)");
+ } else {
+ sb.append(" (running)");
+ }
+ }
pw.println(sb.toString());
return true;
}
@@ -2590,15 +2678,23 @@
long elapsedRealtimeUs, String name, int which, String linePrefix) {
long totalTimeMicros = 0;
int count = 0;
+ long max = -1;
+ long current = -1;
if (timer != null) {
totalTimeMicros = timer.getTotalTimeLocked(elapsedRealtimeUs, which);
count = timer.getCountLocked(which);
+ current = timer.getCurrentDurationMsLocked(elapsedRealtimeUs/1000);
+ max = timer.getMaxDurationMsLocked(elapsedRealtimeUs/1000);
}
sb.append(linePrefix);
sb.append((totalTimeMicros + 500) / 1000); // microseconds to milliseconds with rounding
sb.append(',');
sb.append(name != null ? name + "," : "");
sb.append(count);
+ sb.append(',');
+ sb.append(current);
+ sb.append(',');
+ sb.append(max);
return ",";
}
@@ -3105,20 +3201,22 @@
final long mobilePacketsTx = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which);
final long mobileActiveTime = u.getMobileRadioActiveTime(which);
final int mobileActiveCount = u.getMobileRadioActiveCount(which);
+ final long mobileWakeup = u.getMobileRadioApWakeupCount(which);
final long wifiPacketsRx = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which);
final long wifiPacketsTx = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which);
+ final long wifiWakeup = u.getWifiRadioApWakeupCount(which);
final long btBytesRx = u.getNetworkActivityBytes(NETWORK_BT_RX_DATA, which);
final long btBytesTx = u.getNetworkActivityBytes(NETWORK_BT_TX_DATA, which);
if (mobileBytesRx > 0 || mobileBytesTx > 0 || wifiBytesRx > 0 || wifiBytesTx > 0
|| mobilePacketsRx > 0 || mobilePacketsTx > 0 || wifiPacketsRx > 0
|| wifiPacketsTx > 0 || mobileActiveTime > 0 || mobileActiveCount > 0
- || btBytesRx > 0 || btBytesTx > 0) {
+ || btBytesRx > 0 || btBytesTx > 0 || mobileWakeup > 0 || wifiWakeup > 0) {
dumpLine(pw, uid, category, NETWORK_DATA, mobileBytesRx, mobileBytesTx,
wifiBytesRx, wifiBytesTx,
mobilePacketsRx, mobilePacketsTx,
wifiPacketsRx, wifiPacketsTx,
mobileActiveTime, mobileActiveCount,
- btBytesRx, btBytesTx);
+ btBytesRx, btBytesTx, mobileWakeup, wifiWakeup);
}
// Dump modem controller data, per UID.
@@ -4125,6 +4223,9 @@
final int wifiScanCount = u.getWifiScanCount(which);
final long uidWifiRunningTime = u.getWifiRunningTime(rawRealtime, which);
+ final long mobileWakeup = u.getMobileRadioApWakeupCount(which);
+ final long wifiWakeup = u.getWifiRadioApWakeupCount(which);
+
if (mobileRxBytes > 0 || mobileTxBytes > 0
|| mobileRxPackets > 0 || mobileTxPackets > 0) {
pw.print(prefix); pw.print(" Mobile network: ");
@@ -4150,6 +4251,14 @@
pw.println(sb.toString());
}
+ if (mobileWakeup > 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Mobile radio AP wakeups: ");
+ sb.append(mobileWakeup);
+ pw.println(sb.toString());
+ }
+
printControllerActivityIfInteresting(pw, sb, prefix + " ", "Modem",
u.getModemControllerActivity(), which);
@@ -4181,6 +4290,14 @@
pw.println(sb.toString());
}
+ if (wifiWakeup > 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" WiFi AP wakeups: ");
+ sb.append(wifiWakeup);
+ pw.println(sb.toString());
+ }
+
printControllerActivityIfInteresting(pw, sb, prefix + " ", "WiFi",
u.getWifiControllerActivity(), which);
@@ -4922,7 +5039,9 @@
pw.print(',');
pw.print(rec.stepDetails.statIdlTime);
pw.print(',');
- pw.print(rec.stepDetails.statPlatformIdleState);
+ if (rec.stepDetails.statPlatformIdleState != null) {
+ pw.print(rec.stepDetails.statPlatformIdleState);
+ }
pw.println();
}
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index dc7be6b..3d3dc9c 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -669,9 +669,54 @@
public static final int M = 23;
/**
- * N is for ¯\_(ツ)_/¯.
+ * N is for Nougat.
+ *
+ * <p>Applications targeting this or a later release will get these
+ * new changes in behavior:</p>
+ * <ul>
+ * <li> {@link android.app.DownloadManager.Request#setAllowedNetworkTypes
+ * DownloadManager.Request.setAllowedNetworkTypes}
+ * will disable "allow over metered" when specifying only
+ * {@link android.app.DownloadManager.Request#NETWORK_WIFI}.</li>
+ * <li> {@link android.app.DownloadManager} no longer allows access to raw
+ * file paths.</li>
+ * <li> {@link android.app.Notification.Builder#setShowWhen
+ * Notification.Builder.setShowWhen}
+ * must be called explicitly to have the time shown, and various other changes in
+ * {@link android.app.Notification.Builder Notification.Builder} to how notifications
+ * are shown.</li>
+ * <li>{@link android.content.Context#MODE_WORLD_READABLE} and
+ * {@link android.content.Context#MODE_WORLD_WRITEABLE} are no longer supported.</li>
+ * <li>{@link android.os.FileUriExposedException} will be thrown to applications.</li>
+ * <li>Applications will see global drag and drops as per
+ * {@link android.view.View#DRAG_FLAG_GLOBAL}.</li>
+ * <li>{@link android.webkit.WebView#evaluateJavascript WebView.evaluateJavascript}
+ * will not persist state from an empty WebView.</li>
+ * <li>{@link android.animation.AnimatorSet} will not ignore calls to end() before
+ * start().</li>
+ * <li>{@link android.app.AlarmManager#cancel(android.app.PendingIntent)
+ * AlarmManager.cancel} will throw a NullPointerException if given a null operation.</li>
+ * <li>{@link android.app.FragmentManager} will ensure fragments have been created
+ * before being placed on the back stack.</li>
+ * <li>{@link android.app.FragmentManager} restores fragments in
+ * {@link android.app.Fragment#onCreate Fragment.onCreate} rather than after the
+ * method returns.</li>
+ * <li>{@link android.R.attr#resizeableActivity} defaults to true.</li>
+ * <li>{@link android.graphics.drawable.AnimatedVectorDrawable} throws exceptions when
+ * opening invalid VectorDrawable animations.</li>
+ * <li>{@link android.view.ViewGroup.MarginLayoutParams} will no longer be dropped
+ * when converting between some types of layout params (such as
+ * {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams} to
+ * {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams}).</li>
+ * <li>Your application processes will not be killed when the device density changes.</li>
+ * </ul>
*/
public static final int N = 24;
+
+ /**
+ * N MR1: Nougat++.
+ */
+ public static final int N_MR1 = 25;
}
/** The type of build, like "user" or "eng". */
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index d8be2b6..4616af8 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -286,6 +286,11 @@
}
/** {@hide} */
+ public static File getReferenceProfile(String packageName) {
+ return buildPath(getDataDirectory(), "misc", "profiles", "ref", packageName);
+ }
+
+ /** {@hide} */
public static File getDataProfilesDePackageDirectory(int userId, String packageName) {
return buildPath(getDataProfilesDeDirectory(userId), packageName);
}
@@ -340,6 +345,41 @@
}
/**
+ * Return preloads directory.
+ * <p>This directory may contain pre-loaded content such as
+ * {@link #getDataPreloadsDemoDirectory() demo videos} and
+ * {@link #getDataPreloadsAppsDirectory() APK files} .
+ * {@hide}
+ */
+ public static File getDataPreloadsDirectory() {
+ return new File(getDataDirectory(), "preloads");
+ }
+
+ /**
+ * @see #getDataPreloadsDirectory()
+ * {@hide}
+ */
+ public static File getDataPreloadsDemoDirectory() {
+ return new File(getDataPreloadsDirectory(), "demo");
+ }
+
+ /**
+ * @see #getDataPreloadsDirectory()
+ * {@hide}
+ */
+ public static File getDataPreloadsAppsDirectory() {
+ return new File(getDataPreloadsDirectory(), "apps");
+ }
+
+ /**
+ * @see #getDataPreloadsDirectory()
+ * {@hide}
+ */
+ public static File getDataPreloadsMediaDirectory() {
+ return new File(getDataPreloadsDirectory(), "media");
+ }
+
+ /**
* Return the primary shared/external storage directory. This directory may
* not currently be accessible if it has been mounted by the user on their
* computer, has been removed from the device, or some other problem has
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 36ba696..0b548d5 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -18,6 +18,7 @@
package android.os;
import android.net.InterfaceConfiguration;
+import android.net.INetd;
import android.net.INetworkManagementEventObserver;
import android.net.Network;
import android.net.NetworkStats;
@@ -36,7 +37,7 @@
**/
/**
- * Register an observer to receive events
+ * Register an observer to receive events.
*/
void registerObserver(INetworkManagementEventObserver obs);
@@ -46,6 +47,11 @@
void unregisterObserver(INetworkManagementEventObserver obs);
/**
+ * Retrieve an INetd to talk to netd.
+ */
+ INetd getNetdService();
+
+ /**
* Returns a list of currently known network interfaces
*/
String[] listInterfaces();
@@ -436,6 +442,7 @@
void addInterfaceToLocalNetwork(String iface, in List<RouteInfo> routes);
void removeInterfaceFromLocalNetwork(String iface);
+ int removeRoutesFromLocalNetwork(in List<RouteInfo> routes);
void setAllowOnlyVpnForUids(boolean enable, in UidRange[] uidRanges);
}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index b27cb32..eeb641d 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -81,4 +81,5 @@
void clearSeedAccountData();
boolean someUserHasSeedAccount(in String accountName, in String accountType);
boolean isManagedProfile(int userId);
+ boolean isDemoUser(int userId);
}
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index b58ff1f..d299672 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -147,7 +147,7 @@
}
final long traceTag = me.mTraceTag;
- if (traceTag != 0) {
+ if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 74dcc07..f6e6ad6 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -36,6 +36,7 @@
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.Serializable;
+import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
@@ -2571,6 +2572,20 @@
return p;
}
+ /** @hide */
+ public final <T extends Parcelable> T[] readParcelableArray(ClassLoader loader,
+ Class<T> clazz) {
+ int N = readInt();
+ if (N < 0) {
+ return null;
+ }
+ T[] p = (T[]) Array.newInstance(clazz, N);
+ for (int i = 0; i < N; i++) {
+ p[i] = readParcelable(loader);
+ }
+ return p;
+ }
+
/**
* Read and return a new Serializable object from the parcel.
* @return the Serializable object, or null if the Serializable name
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index b3cf710..9bbe8f9 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -57,14 +57,19 @@
/**
* Power hint:
* Interaction: The user is interacting with the device. The corresponding data field must be
- * the expected duration of the fling, or 0 if unknown.
+ * the expected duration of the interaction, or 0 if unknown.
*
- * Sustained Performance Mode: Enable/Disables Sustained Performance Mode.
+ * Sustained Performance Mode: The corresponding data field must be Enable/Disable
+ * Sustained Performance Mode.
+ *
+ * Launch: This is specific for activity launching. The corresponding data field must be
+ * the expected duration of the required boost, or 0 if unknown.
*
* These must be kept in sync with the values in hardware/libhardware/include/hardware/power.h
*/
public static final int POWER_HINT_INTERACTION = 2;
public static final int POWER_HINT_SUSTAINED_PERFORMANCE_MODE = 6;
+ public static final int POWER_HINT_LAUNCH = 8;
public static String wakefulnessToString(int wakefulness) {
switch (wakefulness) {
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 21b3f6e..e1b7fda 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -16,9 +16,11 @@
package android.os;
+import android.annotation.TestApi;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.system.Os;
+import android.system.OsConstants;
import android.util.Log;
import com.android.internal.os.Zygote;
import dalvik.system.VMRuntime;
@@ -322,6 +324,12 @@
*/
public static final int SCHED_IDLE = 5;
+ /**
+ * Reset scheduler choice on fork.
+ * @hide
+ */
+ public static final int SCHED_RESET_ON_FORK = 0x40000000;
+
// Keep in sync with SP_* constants of enum type SchedPolicy
// declared in system/core/include/cutils/sched_policy.h,
// except THREAD_GROUP_DEFAULT does not correspond to any SP_* value.
@@ -973,6 +981,9 @@
* priority.
* If the thread is a thread group leader, that is it's gettid() == getpid(),
* then the other threads in the same thread group are _not_ affected.
+ *
+ * Does not set cpuset for some historical reason, just calls
+ * libcutils::set_sched_policy().
*/
public static final native void setThreadGroup(int tid, int group)
throws IllegalArgumentException, SecurityException;
@@ -994,6 +1005,8 @@
* priority threads alone. group == THREAD_GROUP_BG_NONINTERACTIVE moves all
* threads, regardless of priority, to the background scheduling group.
* group == THREAD_GROUP_FOREGROUND is not allowed.
+ *
+ * Always sets cpusets.
*/
public static final native void setProcessGroup(int pid, int group)
throws IllegalArgumentException, SecurityException;
@@ -1065,6 +1078,24 @@
throws IllegalArgumentException;
/**
+ * Return the current scheduling policy of a thread, based on Linux.
+ *
+ * @param tid The identifier of the thread/process to get the scheduling policy.
+ *
+ * @throws IllegalArgumentException Throws IllegalArgumentException if
+ * <var>tid</var> does not exist, or if <var>priority</var> is out of range for the policy.
+ * @throws SecurityException Throws SecurityException if your process does
+ * not have permission to modify the given thread, or to use the given
+ * scheduling policy or priority.
+ *
+ * {@hide}
+ */
+
+ @TestApi
+ public static final native int getThreadScheduler(int tid)
+ throws IllegalArgumentException;
+
+ /**
* Set the scheduling policy and priority of a thread, based on Linux.
*
* @param tid The identifier of the thread/process to change.
@@ -1079,6 +1110,7 @@
*
* {@hide}
*/
+
public static final native void setThreadScheduler(int tid, int policy, int priority)
throws IllegalArgumentException;
@@ -1252,4 +1284,26 @@
* @hide
*/
public static final native void removeAllProcessGroups();
+
+ /**
+ * Check to see if a thread belongs to a given process. This may require
+ * more permissions than apps generally have.
+ * @return true if this thread belongs to a process
+ * @hide
+ */
+ public static final boolean isThreadInProcess(int tid, int pid) {
+ StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+ try {
+ if (Os.access("/proc/" + tid + "/task/" + pid, OsConstants.F_OK)) {
+ return true;
+ } else {
+ return false;
+ }
+ } catch (Exception e) {
+ return false;
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
+
+ }
}
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index dd7be53..507379b 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -570,18 +570,19 @@
* @throws SecurityException if the current user is not allowed to wipe data.
*/
public static void rebootWipeUserData(Context context) throws IOException {
- rebootWipeUserData(context, false, context.getPackageName());
+ rebootWipeUserData(context, false /* shutdown */, context.getPackageName(),
+ false /* force */);
}
/** {@hide} */
public static void rebootWipeUserData(Context context, String reason) throws IOException {
- rebootWipeUserData(context, false, reason);
+ rebootWipeUserData(context, false /* shutdown */, reason, false /* force */);
}
/** {@hide} */
public static void rebootWipeUserData(Context context, boolean shutdown)
throws IOException {
- rebootWipeUserData(context, shutdown, context.getPackageName());
+ rebootWipeUserData(context, shutdown, context.getPackageName(), false /* force */);
}
/**
@@ -595,6 +596,9 @@
* @param shutdown if true, the device will be powered down after
* the wipe completes, rather than being rebooted
* back to the regular system.
+ * @param reason the reason for the wipe that is visible in the logs
+ * @param force whether the {@link UserManager.DISALLOW_FACTORY_RESET} user restriction
+ * should be ignored
*
* @throws IOException if writing the recovery command file
* fails, or if the reboot itself fails.
@@ -602,10 +606,10 @@
*
* @hide
*/
- public static void rebootWipeUserData(Context context, boolean shutdown, String reason)
- throws IOException {
+ public static void rebootWipeUserData(Context context, boolean shutdown, String reason,
+ boolean force) throws IOException {
UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
- if (um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
+ if (!force && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
throw new SecurityException("Wiping data is not allowed for this user.");
}
final ConditionVariable condition = new ConditionVariable();
@@ -658,6 +662,31 @@
}
/**
+ * Reboot into recovery and wipe the A/B device.
+ *
+ * @param Context the Context to use.
+ * @param packageFile the wipe package to be applied.
+ * @param reason the reason to wipe.
+ *
+ * @throws IOException if something goes wrong.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static void rebootWipeAb(Context context, File packageFile, String reason)
+ throws IOException {
+ String reasonArg = null;
+ if (!TextUtils.isEmpty(reason)) {
+ reasonArg = "--reason=" + sanitizeArg(reason);
+ }
+
+ final String filename = packageFile.getCanonicalPath();
+ final String filenameArg = "--wipe_package=" + filename;
+ final String localeArg = "--locale=" + Locale.getDefault().toString();
+ bootCommand(context, "--wipe_ab", filenameArg, reasonArg, localeArg);
+ }
+
+ /**
* Reboot into the recovery system with the supplied argument.
* @param args to pass to the recovery utility.
* @throws IOException if something goes wrong.
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index bcc8d46..03d3cf2 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -580,6 +580,16 @@
public static final String DISALLOW_CAMERA = "no_camera";
/**
+ * Specifies if a user is not allowed to unmute the device's master volume.
+ *
+ * @see DevicePolicyManager#setMasterVolumeMuted(ComponentName, boolean)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ * @hide
+ */
+ public static final String DISALLLOW_UNMUTE_DEVICE = "disallow_unmute_device";
+
+ /**
* Specifies if a user is not allowed to use cellular data when roaming. This can only be set by
* device owners. The default value is <code>false</code>.
*
@@ -603,6 +613,18 @@
public static final String DISALLOW_SET_USER_ICON = "no_set_user_icon";
/**
+ * Specifies if a user is not allowed to enable the oem unlock setting. The default value is
+ * <code>false</code>. Setting this restriction has no effect if the bootloader is already
+ * unlocked.
+ *
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ * @hide
+ */
+ public static final String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
+
+ /**
* Allows apps in the parent profile to handle web links from the managed profile.
*
* This user restriction has an effect only in a managed profile.
@@ -789,7 +811,7 @@
*/
public boolean isPrimaryUser() {
UserInfo user = getUserInfo(UserHandle.myUserId());
- return user != null ? user.isPrimary() : false;
+ return user != null && user.isPrimary();
}
/**
@@ -855,7 +877,21 @@
*/
public boolean isGuestUser() {
UserInfo user = getUserInfo(UserHandle.myUserId());
- return user != null ? user.isGuest() : false;
+ return user != null && user.isGuest();
+ }
+
+ /**
+ * Checks if the calling app is running in a demo user. When running in a demo user,
+ * apps can be more helpful to the user, or explain their features in more detail.
+ *
+ * @return whether the caller is a demo user.
+ */
+ public boolean isDemoUser() {
+ try {
+ return mService.isDemoUser(UserHandle.myUserId());
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
}
/**
@@ -1975,6 +2011,10 @@
if (!supportsMultipleUsers()) {
return false;
}
+ // If Demo Mode is on, don't show user switcher
+ if (isDeviceInDemoMode(mContext)) {
+ return false;
+ }
List<UserInfo> users = getUsers(true);
if (users == null) {
return false;
@@ -1991,6 +2031,14 @@
}
/**
+ * @hide
+ */
+ public static boolean isDeviceInDemoMode(Context context) {
+ return Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.DEVICE_DEMO_MODE, 0) > 0;
+ }
+
+ /**
* Returns a serial number on this device for a given userHandle. User handles can be recycled
* when deleting and creating users, but serial numbers are not reused until the device is wiped.
* @param userHandle
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 485bbd1..c5507b9 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SdkConstant;
import android.app.ActivityThread;
import android.content.ContentResolver;
import android.content.Context;
@@ -45,8 +46,11 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.util.Preconditions;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
@@ -92,6 +96,19 @@
/** {@hide} */
public static final String UUID_PRIMARY_PHYSICAL = "primary_physical";
+
+ /**
+ * Activity Action: Allows the user to manage their storage. This activity provides the ability
+ * to free up space on the device by deleting data such as apps.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MANAGE_STORAGE
+ = "android.os.storage.action.MANAGE_STORAGE";
+
/** {@hide} */
public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0;
/** {@hide} */
@@ -116,6 +133,15 @@
private static volatile IMountService sMountService = null;
+ // TODO: the location of the primary storage block varies from device to device, so we need to
+ // try the most likely candidates - a long-term solution would be a device-specific vold
+ // function that returns the calculated size.
+ private static final String[] INTERNAL_STORAGE_SIZE_PATHS = {
+ "/sys/block/mmcblk0/size",
+ "/sys/block/sda/size"
+ };
+ private static final int INTERNAL_STORAGE_SECTOR_SIZE = 512;
+
private final Context mContext;
private final ContentResolver mResolver;
@@ -903,6 +929,27 @@
return getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0];
}
+ /** {@hide} */
+ public long getPrimaryStorageSize() {
+ for (String path : INTERNAL_STORAGE_SIZE_PATHS) {
+ final long numberBlocks = readLong(path);
+ if (numberBlocks > 0) {
+ return numberBlocks * INTERNAL_STORAGE_SECTOR_SIZE;
+ }
+ }
+ return 0;
+ }
+
+ private long readLong(String path) {
+ try (final FileInputStream fis = new FileInputStream(path);
+ final BufferedReader reader = new BufferedReader(new InputStreamReader(fis));) {
+ return Long.parseLong(reader.readLine());
+ } catch (Exception e) {
+ Slog.w(TAG, "Could not read " + path, e);
+ return 0;
+ }
+ }
+
/** @removed */
public @NonNull StorageVolume[] getVolumeList() {
return getVolumeList(mContext.getUserId(), 0);
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index 3e496b6..73fa01e 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -27,12 +27,14 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnKeyListener;
import android.view.ViewGroup;
import android.widget.ListView;
+import android.widget.TextView;
/**
* Shows a hierarchy of {@link Preference} objects as
@@ -189,12 +191,10 @@
0);
ListView lv = (ListView) view.findViewById(android.R.id.list);
- if (lv != null) {
- Drawable divider =
- a.getDrawable(com.android.internal.R.styleable.PreferenceFragment_divider);
- if (divider != null) {
- lv.setDivider(divider);
- }
+ if (lv != null
+ && a.hasValueOrEmpty(com.android.internal.R.styleable.PreferenceFragment_divider)) {
+ lv.setDivider(
+ a.getDrawable(com.android.internal.R.styleable.PreferenceFragment_divider));
}
a.recycle();
@@ -368,6 +368,20 @@
private void bindPreferences() {
final PreferenceScreen preferenceScreen = getPreferenceScreen();
if (preferenceScreen != null) {
+ View root = getView();
+ if (root != null) {
+ View titleView = root.findViewById(android.R.id.title);
+ if (titleView instanceof TextView) {
+ CharSequence title = preferenceScreen.getTitle();
+ if (TextUtils.isEmpty(title)) {
+ titleView.setVisibility(View.GONE);
+ } else {
+ ((TextView) titleView).setText(title);
+ titleView.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
preferenceScreen.bind(getListView());
}
onBindPreferences();
diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java
index b1317e6..2305b05 100644
--- a/core/java/android/preference/PreferenceScreen.java
+++ b/core/java/android/preference/PreferenceScreen.java
@@ -19,6 +19,8 @@
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -31,6 +33,7 @@
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
+import android.widget.TextView;
/**
* Represents a top-level {@link Preference} that
@@ -91,13 +94,33 @@
private Dialog mDialog;
private ListView mListView;
-
+
+ private int mLayoutResId = com.android.internal.R.layout.preference_list_fragment;
+ private Drawable mDividerDrawable;
+ private boolean mDividerSpecified;
+
/**
* Do NOT use this constructor, use {@link PreferenceManager#createPreferenceScreen(Context)}.
* @hide-
*/
public PreferenceScreen(Context context, AttributeSet attrs) {
super(context, attrs, com.android.internal.R.attr.preferenceScreenStyle);
+
+ TypedArray a = context.obtainStyledAttributes(null,
+ com.android.internal.R.styleable.PreferenceScreen,
+ com.android.internal.R.attr.preferenceScreenStyle,
+ 0);
+
+ mLayoutResId = a.getResourceId(
+ com.android.internal.R.styleable.PreferenceScreen_screenLayout,
+ mLayoutResId);
+ if (a.hasValueOrEmpty(com.android.internal.R.styleable.PreferenceScreen_divider)) {
+ mDividerDrawable =
+ a.getDrawable(com.android.internal.R.styleable.PreferenceScreen_divider);
+ mDividerSpecified = true;
+ }
+
+ a.recycle();
}
/**
@@ -163,18 +186,30 @@
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View childPrefScreen = inflater.inflate(
- com.android.internal.R.layout.preference_list_fragment, null);
+ View childPrefScreen = inflater.inflate(mLayoutResId, null);
+ View titleView = childPrefScreen.findViewById(android.R.id.title);
mListView = (ListView) childPrefScreen.findViewById(android.R.id.list);
+ if (mDividerSpecified) {
+ mListView.setDivider(mDividerDrawable);
+ }
+
bind(mListView);
// Set the title bar if title is available, else no title bar
final CharSequence title = getTitle();
Dialog dialog = mDialog = new Dialog(context, context.getThemeResId());
if (TextUtils.isEmpty(title)) {
+ if (titleView != null) {
+ titleView.setVisibility(View.GONE);
+ }
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
} else {
- dialog.setTitle(title);
+ if (titleView instanceof TextView) {
+ ((TextView) titleView).setText(title);
+ titleView.setVisibility(View.VISIBLE);
+ } else {
+ dialog.setTitle(title);
+ }
}
dialog.setContentView(childPrefScreen);
dialog.setOnDismissListener(this);
diff --git a/core/java/android/print/PrintServiceRecommendationsLoader.java b/core/java/android/print/PrintServiceRecommendationsLoader.java
index bb5d065..c6a4d51 100644
--- a/core/java/android/print/PrintServiceRecommendationsLoader.java
+++ b/core/java/android/print/PrintServiceRecommendationsLoader.java
@@ -36,7 +36,7 @@
private final @NonNull PrintManager mPrintManager;
/** Handler to sequentialize the delivery of the results to the main thread */
- private final Handler mHandler;
+ private final @NonNull Handler mHandler;
/** Listens for updates to the data from the platform */
private PrintManager.PrintServiceRecommendationsChangeListener mListener;
@@ -90,9 +90,7 @@
mListener = null;
}
- if (mHandler != null) {
- mHandler.removeMessages(0);
- }
+ mHandler.removeMessages(0);
}
@Override
diff --git a/core/java/android/print/PrintServicesLoader.java b/core/java/android/print/PrintServicesLoader.java
index 60d7d66..4c9a69a 100644
--- a/core/java/android/print/PrintServicesLoader.java
+++ b/core/java/android/print/PrintServicesLoader.java
@@ -39,7 +39,7 @@
private final @NonNull PrintManager mPrintManager;
/** Handler to sequentialize the delivery of the results to the main thread */
- private Handler mHandler;
+ private final @NonNull Handler mHandler;
/** Listens for updates to the data from the platform */
private PrintManager.PrintServicesChangeListener mListener;
@@ -54,6 +54,7 @@
public PrintServicesLoader(@NonNull PrintManager printManager, @NonNull Context context,
int selectionFlags) {
super(Preconditions.checkNotNull(context));
+ mHandler = new MyHandler();
mPrintManager = Preconditions.checkNotNull(printManager);
mSelectionFlags = Preconditions.checkFlagsArgument(selectionFlags,
PrintManager.ALL_SERVICES);
@@ -75,7 +76,6 @@
@Override
protected void onStartLoading() {
- mHandler = new MyHandler();
mListener = new PrintManager.PrintServicesChangeListener() {
@Override public void onPrintServicesChanged() {
queueNewResult();
@@ -95,10 +95,7 @@
mListener = null;
}
- if (mHandler != null) {
- mHandler.removeMessages(0);
- mHandler = null;
- }
+ mHandler.removeMessages(0);
}
@Override
@@ -119,8 +116,6 @@
@Override
public void handleMessage(Message msg) {
- super.handleMessage(msg);
-
if (isStarted()) {
deliverResult((List<PrintServiceInfo>) msg.obj);
}
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 893eb37..fbd61cf3 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -179,6 +179,7 @@
* <li>{@link #VOICEMAIL_TYPE}</li>
* <li>{@link #REJECTED_TYPE}</li>
* <li>{@link #BLOCKED_TYPE}</li>
+ * <li>{@link #ANSWERED_EXTERNALLY_TYPE}</li>
* </ul>
* </p>
*/
@@ -200,7 +201,6 @@
* Call log type for a call which was answered on another device. Used in situations where
* a call rings on multiple devices simultaneously and it ended up being answered on a
* device other than the current one.
- * @hide
*/
public static final int ANSWERED_EXTERNALLY_TYPE = 7;
@@ -214,10 +214,7 @@
/** Call had video. */
public static final int FEATURES_VIDEO = 0x1;
- /**
- * Call was pulled externally.
- * @hide
- */
+ /** Call was pulled externally. */
public static final int FEATURES_PULLED_EXTERNALLY = 0x2;
/**
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 1158776..d587ba8 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -235,7 +235,6 @@
* @see #FLAG_DIR_PREFERS_GRID
* @see #FLAG_DIR_PREFERS_LAST_MODIFIED
* @see #FLAG_VIRTUAL_DOCUMENT
- * @see #FLAG_ARCHIVE
* @see #FLAG_SUPPORTS_COPY
* @see #FLAG_SUPPORTS_MOVE
* @see #FLAG_SUPPORTS_REMOVE
@@ -326,7 +325,7 @@
* Flag indicating that a document can be renamed.
*
* @see #COLUMN_FLAGS
- * @see DocumentsContract#renameDocument(ContentProviderClient, Uri,
+ * @see DocumentsContract#renameDocument(ContentResolver, Uri,
* String)
* @see DocumentsProvider#renameDocument(String, String)
*/
@@ -337,7 +336,7 @@
* within the same document provider.
*
* @see #COLUMN_FLAGS
- * @see DocumentsContract#copyDocument(ContentProviderClient, Uri, Uri)
+ * @see DocumentsContract#copyDocument(ContentResolver, Uri, Uri)
* @see DocumentsProvider#copyDocument(String, String)
*/
public static final int FLAG_SUPPORTS_COPY = 1 << 7;
@@ -347,7 +346,7 @@
* within the same document provider.
*
* @see #COLUMN_FLAGS
- * @see DocumentsContract#moveDocument(ContentProviderClient, Uri, Uri, Uri)
+ * @see DocumentsContract#moveDocument(ContentResolver, Uri, Uri, Uri)
* @see DocumentsProvider#moveDocument(String, String, String)
*/
public static final int FLAG_SUPPORTS_MOVE = 1 << 8;
@@ -368,7 +367,7 @@
* Flag indicating that a document can be removed from a parent.
*
* @see #COLUMN_FLAGS
- * @see DocumentsContract#removeDocument(ContentProviderClient, Uri, Uri)
+ * @see DocumentsContract#removeDocument(ContentResolver, Uri, Uri)
* @see DocumentsProvider#removeDocument(String, String)
*/
public static final int FLAG_SUPPORTS_REMOVE = 1 << 10;
@@ -870,7 +869,7 @@
* Test if the given URI represents a {@link Document} tree.
*
* @see #buildTreeDocumentUri(String, String)
- * @see #getTreeDocumentId(Uri, String)
+ * @see #getTreeDocumentId(Uri)
*/
public static boolean isTreeUri(Uri uri) {
final List<String> paths = uri.getPathSegments();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9c567a9..300deea 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -440,6 +440,22 @@
"android.settings.DISPLAY_SETTINGS";
/**
+ * Activity Action: Show settings to allow configuration of Night display.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_NIGHT_DISPLAY_SETTINGS =
+ "android.settings.NIGHT_DISPLAY_SETTINGS";
+
+ /**
* Activity Action: Show settings to allow configuration of locale.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
@@ -5295,15 +5311,6 @@
"accessibility_display_daltonizer";
/**
- * Float list that specifies the color matrix to apply to
- * the display. Valid values are defined in AccessibilityManager.
- *
- * @hide
- */
- public static final String ACCESSIBILITY_DISPLAY_COLOR_MATRIX =
- "accessibility_display_color_matrix";
-
- /**
* Setting that specifies whether automatic click when the mouse pointer stops moving is
* enabled.
*
@@ -6012,6 +6019,17 @@
public static final String ASSIST_SCREENSHOT_ENABLED = "assist_screenshot_enabled";
/**
+ * Specifies whether the screen will show an animation if screen contents are sent to the
+ * assist application (active voice interaction service).
+ *
+ * Note that the disclosure will be forced for third-party assistants or if the device
+ * does not support disabling it.
+ *
+ * @hide
+ */
+ public static final String ASSIST_DISCLOSURE_ENABLED = "assist_disclosure_enabled";
+
+ /**
* Names of the service components that the current user has explicitly allowed to
* see all of the user's notifications, separated by ':'.
*
@@ -6147,44 +6165,39 @@
"camera_double_tap_power_gesture_disabled";
/**
-
- /**
- * Behavior of twilight on the device.
- * One of {@link #TWILIGHT_MODE_LOCKED_OFF}, {@link #TWILIGHT_MODE_LOCKED_ON}
- * or {@link #TWILIGHT_MODE_AUTO}.
+ * Whether the camera double twist gesture to flip between front and back mode should be
+ * enabled.
+ *
* @hide
*/
- public static final String TWILIGHT_MODE = "twilight_mode";
+ public static final String CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED =
+ "camera_double_twist_to_flip_enabled";
/**
- * Twilight mode always off.
+ * Control whether Night display is currently activated.
* @hide
*/
- public static final int TWILIGHT_MODE_LOCKED_OFF = 0;
+ public static final String NIGHT_DISPLAY_ACTIVATED = "night_display_activated";
/**
- * Twilight mode always on.
+ * Control whether Night display will automatically activate/deactivate.
* @hide
*/
- public static final int TWILIGHT_MODE_LOCKED_ON = 1;
+ public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode";
/**
- * Twilight mode auto.
+ * Custom time when Night display is scheduled to activate.
+ * Represented as milliseconds from midnight (e.g. 79200000 == 10pm).
* @hide
*/
- public static final int TWILIGHT_MODE_AUTO = 2;
+ public static final String NIGHT_DISPLAY_CUSTOM_START_TIME = "night_display_custom_start_time";
/**
- * Twilight mode auto, temporarily overriden to on.
+ * Custom time when Night display is scheduled to deactivate.
+ * Represented as milliseconds from midnight (e.g. 21600000 == 6am).
* @hide
*/
- public static final int TWILIGHT_MODE_AUTO_OVERRIDE_OFF = 3;
-
- /**
- * Twilight mode auto, temporarily overriden to off.
- * @hide
- */
- public static final int TWILIGHT_MODE_AUTO_OVERRIDE_ON = 4;
+ public static final String NIGHT_DISPLAY_CUSTOM_END_TIME = "night_display_custom_end_time";
/**
* Whether brightness should automatically adjust based on twilight state.
@@ -6228,6 +6241,20 @@
public static final int VR_DISPLAY_MODE_OFF = 1;
/**
+ * Whether CarrierAppUtils#disableCarrierAppsUntilPrivileged has been executed at least
+ * once.
+ *
+ * <p>This is used to ensure that we only take one pass which will disable apps that are not
+ * privileged (if any). From then on, we only want to enable apps (when a matching SIM is
+ * inserted), to avoid disabling an app that the user might actively be using.
+ *
+ * <p>Will be set to 1 once executed.
+ *
+ * @hide
+ */
+ public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled";
+
+ /**
* Whether parent user can access remote contact in managed profile.
*
* @hide
@@ -6236,6 +6263,67 @@
"managed_profile_contact_remote_search";
/**
+ * Whether or not the automatic storage manager is enabled and should run on the device.
+ *
+ * @hide
+ */
+ public static final String AUTOMATIC_STORAGE_MANAGER_ENABLED =
+ "automatic_storage_manager_enabled";
+
+ /**
+ * How many days of information for the automatic storage manager to retain on the device.
+ *
+ * @hide
+ */
+ public static final String AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN =
+ "automatic_storage_manager_days_to_retain";
+
+ /**
+ * Default number of days of information for the automatic storage manager to retain.
+ *
+ * @hide
+ */
+ public static final int AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_DEFAULT = 90;
+
+ /**
+ * How many bytes the automatic storage manager has cleared out.
+ *
+ * @hide
+ */
+ public static final String AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED =
+ "automatic_storage_manager_bytes_cleared";
+
+
+ /**
+ * Last run time for the automatic storage manager.
+ *
+ * @hide
+ */
+ public static final String AUTOMATIC_STORAGE_MANAGER_LAST_RUN =
+ "automatic_storage_manager_last_run";
+
+
+ /**
+ * Whether SystemUI navigation keys is enabled.
+ * @hide
+ */
+ public static final String SYSTEM_NAVIGATION_KEYS_ENABLED =
+ "system_navigation_keys_enabled";
+
+ /**
+ * Holds comma separated list of ordering of QS tiles.
+ * @hide
+ */
+ public static final String QS_TILES = "sysui_qs_tiles";
+
+ /**
+ * Whether preloaded APKs have been installed for the user.
+ * @hide
+ */
+ public static final String DEMO_USER_SETUP_COMPLETE
+ = "demo_user_setup_complete";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -6252,7 +6340,6 @@
USB_MASS_STORAGE_ENABLED, // moved to global
ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
ACCESSIBILITY_DISPLAY_DALTONIZER,
- ACCESSIBILITY_DISPLAY_COLOR_MATRIX,
ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
@@ -6308,7 +6395,15 @@
PREFERRED_TTY_MODE,
ENHANCED_VOICE_PRIVACY_ENABLED,
TTY_MODE_ENABLED,
- INCALL_POWER_BUTTON_BEHAVIOR
+ INCALL_POWER_BUTTON_BEHAVIOR,
+ NIGHT_DISPLAY_CUSTOM_START_TIME,
+ NIGHT_DISPLAY_CUSTOM_END_TIME,
+ NIGHT_DISPLAY_AUTO_MODE,
+ NIGHT_DISPLAY_ACTIVATED,
+ CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
+ CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
+ SYSTEM_NAVIGATION_KEYS_ENABLED,
+ QS_TILES,
};
/**
@@ -8473,8 +8568,6 @@
/**
* The name of the device
- *
- * @hide
*/
public static final String DEVICE_NAME = "device_name";
@@ -8558,6 +8651,24 @@
"ephemeral_cookie_max_size_bytes";
/**
+ * A mask applied to the ephemeral hash to generate the hash prefix.
+ * <p>
+ * Type: int
+ *
+ * @hide
+ */
+ public static final String EPHEMERAL_HASH_PREFIX_MASK = "ephemeral_hash_prefix_mask";
+
+ /**
+ * Number of hash prefixes to send during ephemeral resolution.
+ * <p>
+ * Type: int
+ *
+ * @hide
+ */
+ public static final String EPHEMERAL_HASH_PREFIX_COUNT = "ephemeral_hash_prefix_count";
+
+ /**
* The duration for caching uninstalled ephemeral apps.
* <p>
* Type: long
@@ -8593,6 +8704,31 @@
public static final String SAFE_BOOT_DISALLOWED = "safe_boot_disallowed";
/**
+ * Whether this device is currently in retail demo mode. If true, device
+ * usage is severely limited.
+ * <p>
+ * Type: int (0 for false, 1 for true)
+ * @hide
+ */
+ public static final String DEVICE_DEMO_MODE = "device_demo_mode";
+
+ /**
+ * Retail mode specific settings. This is encoded as a key=value list, separated by commas.
+ * Ex: "user_inactivity_timeout_ms=30000,warning_dialog_timeout_ms=10000". The following
+ * keys are supported:
+ *
+ * <pre>
+ * user_inactivity_timeout_ms (long)
+ * warning_dialog_timeout_ms (long)
+ * </pre>
+ * <p>
+ * Type: string
+ *
+ * @hide
+ */
+ public static final String RETAIL_DEMO_MODE_CONSTANTS = "retail_demo_mode_constants";
+
+ /**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.
*
@@ -8997,6 +9133,12 @@
* @hide
*/
public static final String MAX_NOTIFICATION_ENQUEUE_RATE = "max_notification_enqueue_rate";
+
+ /**
+ * Whether cell is enabled/disabled
+ * @hide
+ */
+ public static final String CELL_ON = "cell_on";
}
/**
diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java
index 6a3cc02..5099eeb 100644
--- a/core/java/android/provider/VoicemailContract.java
+++ b/core/java/android/provider/VoicemailContract.java
@@ -25,13 +25,11 @@
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
-import android.database.Cursor;
import android.net.Uri;
import android.provider.CallLog.Calls;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.Voicemail;
-
import java.util.List;
/**
@@ -107,12 +105,74 @@
public static final String ACTION_SYNC_VOICEMAIL = "android.provider.action.SYNC_VOICEMAIL";
/**
+ * Broadcast intent to inform a new visual voicemail SMS has been received. This intent will
+ * only be delivered to the voicemail client. The intent will have the following extra values:
+ *
+ * <ul>
+ * <li><em>{@link #EXTRA_VOICEMAIL_SMS_TYPE}</em> - (String) The event type of the SMS. Common
+ * values are "SYNC" or "STATUS"</li>
+ * <li><em>{@link #EXTRA_VOICEMAIL_SMS_DATA}</em> - (Bundle) The fields sent by the SMS</li>
+ * <li><em>{@link #EXTRA_VOICEMAIL_SMS_SUBID}</em> - (Integer) The subscription ID of the
+ * phone account that received the SMS</li>
+ * </ul>
+ */
+ /** @hide */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_VOICEMAIL_SMS_RECEIVED =
+ "android.intent.action.VOICEMAIL_SMS_RECEIVED";
+
+ /**
+ * Optional extra included in {@link #ACTION_VOICEMAIL_SMS_RECEIVED} broadcast intents to
+ * indicate the event type of the SMS. Common values are "SYNC" or "STATUS". The extra will not
+ * exist if the framework cannot parse the SMS as voicemail but the carrier pattern indicates
+ * it is.
+ */
+ /** @hide */
+ public static final String EXTRA_VOICEMAIL_SMS_PREFIX =
+ "com.android.voicemail.extra.VOICEMAIL_SMS_PREFIX";
+
+ /**
+ * Optional extra included in {@link #ACTION_VOICEMAIL_SMS_RECEIVED} broadcast intents to
+ * indicate the fields sent by the SMS. The extra will not exist if the framework cannot
+ * parse the SMS as voicemail but the carrier pattern indicates it is.
+ */
+ /** @hide */
+ public static final String EXTRA_VOICEMAIL_SMS_FIELDS =
+ "com.android.voicemail.extra.VOICEMAIL_SMS_FIELDS";
+
+ /**
+ * Extra included in {@link #ACTION_VOICEMAIL_SMS_RECEIVED} broadcast intents to indicate the
+ * message body of the SMS. This extra is included if the framework cannot
+ * parse the SMS as voicemail but the carrier pattern indicates it is.
+ */
+ /**
+ * @hide
+ */
+ public static final String EXTRA_VOICEMAIL_SMS_MESSAGE_BODY =
+ "com.android.voicemail.extra.VOICEMAIL_SMS_MESSAGE_BODY";
+
+ /**
+ * Extra included in {@link #ACTION_VOICEMAIL_SMS_RECEIVED} broadcast intents to indicate he
+ * subscription ID of the phone account that received the SMS.
+ */
+ /** @hide */
+ public static final String EXTRA_VOICEMAIL_SMS_SUBID =
+ "com.android.voicemail.extra.VOICEMAIL_SMS_SUBID";
+
+ /**
* Extra included in {@link Intent#ACTION_PROVIDER_CHANGED} broadcast intents to indicate if the
* receiving package made this change.
*/
public static final String EXTRA_SELF_CHANGE = "com.android.voicemail.extra.SELF_CHANGE";
/**
+ * Extra included in {@link #ACTION_SYNC_VOICEMAIL} broadcast intents to indicate which {@link
+ * PhoneAccountHandle} to sync.
+ */
+ public static final String EXTRA_PHONE_ACCOUNT_HANDLE =
+ "android.provider.extra.PHONE_ACCOUNT_HANDLE";
+
+ /**
* Name of the source package field, which must be same across all voicemail related tables.
* This is an internal field.
* @hide
@@ -360,6 +420,20 @@
*/
public static final String SOURCE_PACKAGE = SOURCE_PACKAGE_FIELD;
+ /**
+ * The type of the source, which determines how to interpret source-specific states.
+ * Typically this will be set to the same string as
+ * {@link android.telephony.CarrierConfigManager#KEY_VVM_TYPE_STRING}. For example,
+ * "vvm_type_omtp".
+ *
+ * <P>Type: TEXT</P>
+ *
+ * @see #CONFIGURATION_STATE
+ * @see #DATA_CHANNEL_STATE
+ * @see #NOTIFICATION_CHANNEL_STATE
+ */
+ public static final String SOURCE_TYPE = "source_type";
+
// Note: Multiple entries may exist for a single source if they are differentiated by the
// PHONE_ACCOUNT_* fields.
@@ -392,21 +466,21 @@
public static final String VOICEMAIL_ACCESS_URI = "voicemail_access_uri";
/**
* The configuration state of the voicemail source.
+ *
+ * <P>Negative values are reserved to the source for source-specific states, see
+ * {@link #SOURCE_TYPE}
+ *
* <P> Possible values:
* {@link #CONFIGURATION_STATE_OK},
* {@link #CONFIGURATION_STATE_NOT_CONFIGURED},
* {@link #CONFIGURATION_STATE_CAN_BE_CONFIGURED}
+ * {@link #CONFIGURATION_STATE_CONFIGURING}
+ * {@link #CONFIGURATION_STATE_FAILED}
+ * {@link #CONFIGURATION_STATE_DISABLED}
* <P>Type: INTEGER</P>
*/
public static final String CONFIGURATION_STATE = "configuration_state";
- /**
- * Value of {@link #CONFIGURATION_STATE} passed into
- * {@link #setStatus(Context, PhoneAccountHandle, int, int, int)} to indicate that the
- * {@link #CONFIGURATION_STATE} field is not to be changed
- *
- * @hide
- */
- public static final int CONFIGURATION_STATE_IGNORE = -1;
+
/** Value of {@link #CONFIGURATION_STATE} to indicate an all OK configuration status. */
public static final int CONFIGURATION_STATE_OK = 0;
/**
@@ -422,8 +496,27 @@
*/
public static final int CONFIGURATION_STATE_CAN_BE_CONFIGURED = 2;
/**
+ * Value of {@link #CONFIGURATION_STATE} to indicate that visual voicemail still is being
+ * configured.
+ */
+ public static final int CONFIGURATION_STATE_CONFIGURING = 3;
+ /**
+ * Value of {@link #CONFIGURATION_STATE} to indicate that visual voicemail has failed to
+ * be configured.
+ */
+ public static final int CONFIGURATION_STATE_FAILED = 4;
+ /**
+ * Value of {@link #CONFIGURATION_STATE} to indicate that visual voicemail is disabled by
+ * the user.
+ */
+ public static final int CONFIGURATION_STATE_DISABLED = 5;
+ /**
* The data channel state of the voicemail source. This the channel through which the source
* pulls voicemail data from a remote server.
+ *
+ * <P>Negative values are reserved to the source for source-specific states, see
+ * {@link #SOURCE_TYPE}
+ *
* <P> Possible values:
* {@link #DATA_CHANNEL_STATE_OK},
* {@link #DATA_CHANNEL_STATE_NO_CONNECTION}
@@ -431,14 +524,7 @@
* <P>Type: INTEGER</P>
*/
public static final String DATA_CHANNEL_STATE = "data_channel_state";
- /**
- * Value of {@link #DATA_CHANNEL_STATE} passed into
- * {@link #setStatus(Context, PhoneAccountHandle, int, int, int)} to indicate that the
- * {@link #DATA_CHANNEL_STATE} field is not to be changed
- *
- * @hide
- */
- public static final int DATA_CHANNEL_STATE_IGNORE = -1;
+
/**
* Value of {@link #DATA_CHANNEL_STATE} to indicate that data channel is working fine.
*/
@@ -478,6 +564,10 @@
/**
* The notification channel state of the voicemail source. This is the channel through which
* the source gets notified of new voicemails on the remote server.
+ *
+ * <P>Negative values are reserved to the source for source-specific states, see
+ * {@link #SOURCE_TYPE}
+ *
* <P> Possible values:
* {@link #NOTIFICATION_CHANNEL_STATE_OK},
* {@link #NOTIFICATION_CHANNEL_STATE_NO_CONNECTION},
@@ -486,14 +576,7 @@
* <P>Type: INTEGER</P>
*/
public static final String NOTIFICATION_CHANNEL_STATE = "notification_channel_state";
- /**
- * Value of {@link #NOTIFICATION_CHANNEL_STATE} passed into
- * {@link #setStatus(Context, PhoneAccountHandle, int, int, int)} to indicate that the
- * {@link #NOTIFICATION_CHANNEL_STATE} field is not to be changed
- *
- * @hide
- */
- public static final int NOTIFICATION_CHANNEL_STATE_IGNORE = -1;
+
/**
* Value of {@link #NOTIFICATION_CHANNEL_STATE} to indicate that the notification channel is
* working fine.
@@ -543,67 +626,5 @@
return Status.CONTENT_URI.buildUpon()
.appendQueryParameter(PARAM_KEY_SOURCE_PACKAGE, packageName).build();
}
-
- /**
- * A helper method to set the status of a voicemail source.
- *
- * @param context The context from the package calling the method. This will be the source.
- * @param accountHandle The handle for the account the source is associated with.
- * @param configurationState See {@link Status#CONFIGURATION_STATE}
- * @param dataChannelState See {@link Status#DATA_CHANNEL_STATE}
- * @param notificationChannelState See {@link Status#NOTIFICATION_CHANNEL_STATE}
- *
- * @hide
- */
- public static void setStatus(Context context, PhoneAccountHandle accountHandle,
- int configurationState, int dataChannelState, int notificationChannelState) {
- ContentValues values = new ContentValues();
- values.put(Status.PHONE_ACCOUNT_COMPONENT_NAME,
- accountHandle.getComponentName().flattenToString());
- values.put(Status.PHONE_ACCOUNT_ID, accountHandle.getId());
- if(configurationState != CONFIGURATION_STATE_IGNORE) {
- values.put(Status.CONFIGURATION_STATE, configurationState);
- }
- if(dataChannelState != DATA_CHANNEL_STATE_IGNORE) {
- values.put(Status.DATA_CHANNEL_STATE, dataChannelState);
- }
- if(notificationChannelState != NOTIFICATION_CHANNEL_STATE_IGNORE) {
- values.put(Status.NOTIFICATION_CHANNEL_STATE, notificationChannelState);
- }
- ContentResolver contentResolver = context.getContentResolver();
- Uri statusUri = buildSourceUri(context.getPackageName());
- contentResolver.insert(statusUri, values);
- }
-
- /**
- * A helper method to set the quota of a voicemail source. Unit is unspecified.
- *
- * @param context The context from the package calling the method. This will be the source.
- * @param accountHandle The handle for the account the source is associated with.
- * @param occupied See {@link Status#QUOTA_OCCUPIED}
- * @param total See {@link Status#QUOTA_TOTAL}
- *
- * @hide
- */
- public static void setQuota(Context context, PhoneAccountHandle accountHandle, int occupied,
- int total) {
- if (occupied == QUOTA_UNAVAILABLE && total == QUOTA_UNAVAILABLE) {
- return;
- }
- ContentValues values = new ContentValues();
- values.put(Status.PHONE_ACCOUNT_COMPONENT_NAME,
- accountHandle.getComponentName().flattenToString());
- values.put(Status.PHONE_ACCOUNT_ID, accountHandle.getId());
- if (occupied != QUOTA_UNAVAILABLE) {
- values.put(Status.QUOTA_OCCUPIED,occupied);
- }
- if (total != QUOTA_UNAVAILABLE) {
- values.put(Status.QUOTA_TOTAL,total);
- }
-
- ContentResolver contentResolver = context.getContentResolver();
- Uri statusUri = buildSourceUri(context.getPackageName());
- contentResolver.insert(statusUri, values);
- }
}
}
diff --git a/core/java/android/service/carrier/CarrierIdentifier.java b/core/java/android/service/carrier/CarrierIdentifier.java
index a70c24d..b47e872 100644
--- a/core/java/android/service/carrier/CarrierIdentifier.java
+++ b/core/java/android/service/carrier/CarrierIdentifier.java
@@ -126,4 +126,13 @@
mGid1 = in.readString();
mGid2 = in.readString();
}
+
+ /** @hide */
+ public interface MatchType {
+ int ALL = 0;
+ int SPN = 1;
+ int IMSI_PREFIX = 2;
+ int GID1 = 3;
+ int GID2 = 4;
+ }
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 6911b01..e90a3ba 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -20,6 +20,8 @@
import android.app.NotificationManager.Policy;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Parcel;
@@ -42,6 +44,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
+import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Objects;
@@ -118,6 +121,7 @@
private static final String RULE_ATT_ZEN = "zen";
private static final String RULE_ATT_CONDITION_ID = "conditionId";
private static final String RULE_ATT_CREATION_TIME = "creationTime";
+ private static final String RULE_ATT_ENABLER = "enabler";
public boolean allowCalls = DEFAULT_ALLOW_CALLS;
public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS;
@@ -502,6 +506,7 @@
rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID);
rt.component = safeComponentName(parser, RULE_ATT_COMPONENT);
rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0);
+ rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER);
rt.condition = readConditionXml(parser);
return rt;
}
@@ -520,6 +525,9 @@
out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString());
}
out.attribute(null, RULE_ATT_CREATION_TIME, Long.toString(rule.creationTime));
+ if (rule.enabler != null) {
+ out.attribute(null, RULE_ATT_ENABLER, rule.enabler);
+ }
if (rule.condition != null) {
writeConditionXml(rule.condition, out);
}
@@ -889,9 +897,13 @@
", endHour=" + endHour +
", endMinute=" + endMinute +
", exitAtAlarm=" + exitAtAlarm +
- ", nextAlarm=" + nextAlarm +
+ ", nextAlarm=" + ts(nextAlarm) +
'}';
}
+
+ protected static String ts(long time) {
+ return new Date(time) + " (" + time + ")";
+ }
}
// ==== Built-in system condition: event ====
@@ -989,6 +1001,25 @@
return UUID.randomUUID().toString().replace("-", "");
}
+ private static String getOwnerCaption(Context context, String owner) {
+ final PackageManager pm = context.getPackageManager();
+ try {
+ final ApplicationInfo info = pm.getApplicationInfo(owner, 0);
+ if (info != null) {
+ final CharSequence seq = info.loadLabel(pm);
+ if (seq != null) {
+ final String str = seq.toString().trim();
+ if (str.length() > 0) {
+ return str;
+ }
+ }
+ }
+ } catch (Throwable e) {
+ Slog.w(TAG, "Error loading owner caption", e);
+ }
+ return "";
+ }
+
public static String getConditionSummary(Context context, ZenModeConfig config,
int userHandle, boolean shortVersion) {
return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion);
@@ -997,23 +1028,28 @@
private static String getConditionLine(Context context, ZenModeConfig config,
int userHandle, boolean useLine1, boolean shortVersion) {
if (config == null) return "";
+ String summary = "";
if (config.manualRule != null) {
final Uri id = config.manualRule.conditionId;
- if (id == null) {
- return context.getString(com.android.internal.R.string.zen_mode_forever);
+ if (config.manualRule.enabler != null) {
+ summary = getOwnerCaption(context, config.manualRule.enabler);
+ } else {
+ if (id == null) {
+ summary = context.getString(com.android.internal.R.string.zen_mode_forever);
+ } else {
+ final long time = tryParseCountdownConditionId(id);
+ Condition c = config.manualRule.condition;
+ if (time > 0) {
+ final long now = System.currentTimeMillis();
+ final long span = time - now;
+ c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS),
+ userHandle, shortVersion);
+ }
+ final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary;
+ summary = TextUtils.isEmpty(rt) ? "" : rt;
+ }
}
- final long time = tryParseCountdownConditionId(id);
- Condition c = config.manualRule.condition;
- if (time > 0) {
- final long now = System.currentTimeMillis();
- final long span = time - now;
- c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS),
- userHandle, shortVersion);
- }
- final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary;
- return TextUtils.isEmpty(rt) ? "" : rt;
}
- String summary = "";
for (ZenRule automaticRule : config.automaticRules.values()) {
if (automaticRule.isAutomaticActive()) {
if (summary.isEmpty()) {
@@ -1023,6 +1059,7 @@
.getString(R.string.zen_mode_rule_name_combination, summary,
automaticRule.name);
}
+
}
}
return summary;
@@ -1038,6 +1075,7 @@
public ComponentName component; // optional
public String id; // required for automatic (unique)
public long creationTime; // required for automatic
+ public String enabler; // package name, only used for manual rules.
public ZenRule() { }
@@ -1055,6 +1093,9 @@
id = source.readString();
}
creationTime = source.readLong();
+ if (source.readInt() == 1) {
+ enabler = source.readString();
+ }
}
@Override
@@ -1083,6 +1124,12 @@
dest.writeInt(0);
}
dest.writeLong(creationTime);
+ if (enabler != null) {
+ dest.writeInt(1);
+ dest.writeString(enabler);
+ } else {
+ dest.writeInt(0);
+ }
}
@Override
@@ -1097,6 +1144,7 @@
.append(",component=").append(component)
.append(",id=").append(id)
.append(",creationTime=").append(creationTime)
+ .append(",enabler=").append(enabler)
.append(']').toString();
}
@@ -1143,6 +1191,9 @@
if (creationTime != to.creationTime) {
d.addLine(item, "creationTime", creationTime, to.creationTime);
}
+ if (enabler != to.enabler) {
+ d.addLine(item, "enabler", enabler, to.enabler);
+ }
}
@Override
@@ -1158,13 +1209,14 @@
&& Objects.equals(other.condition, condition)
&& Objects.equals(other.component, component)
&& Objects.equals(other.id, id)
- && other.creationTime == creationTime;
+ && other.creationTime == creationTime
+ && Objects.equals(other.enabler, enabler);
}
@Override
public int hashCode() {
return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
- component, id, creationTime);
+ component, id, creationTime, enabler);
}
public boolean isAutomaticActive() {
diff --git a/core/java/android/service/persistentdata/PersistentDataBlockManager.java b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
index cfeed51..0f92ed0 100644
--- a/core/java/android/service/persistentdata/PersistentDataBlockManager.java
+++ b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
@@ -163,10 +163,9 @@
/**
* Retrieves available information about this device's flash lock state.
*
- * @return FLASH_LOCK_STATE_LOCKED if device bootloader is locked,
- * FLASH_LOCK_STATE_UNLOCKED if device bootloader is unlocked,
- * or FLASH_LOCK_STATE unknown if this information cannot be ascertained
- * on this device.
+ * @return {@link #FLASH_LOCK_LOCKED} if device bootloader is locked,
+ * {@link #FLASH_LOCK_UNLOCKED} if device bootloader is unlocked, or {@link #FLASH_LOCK_UNKNOWN}
+ * if this information cannot be ascertained on this device.
*/
@FlashLockState
public int getFlashLockState() {
diff --git a/core/java/android/service/quicksettings/IQSService.aidl b/core/java/android/service/quicksettings/IQSService.aidl
index bf96357..d03ff93 100644
--- a/core/java/android/service/quicksettings/IQSService.aidl
+++ b/core/java/android/service/quicksettings/IQSService.aidl
@@ -23,16 +23,16 @@
* @hide
*/
interface IQSService {
- Tile getTile(in ComponentName component);
- void updateQsTile(in Tile tile);
- void updateStatusIcon(in Tile tile, in Icon icon,
+ Tile getTile(in IBinder tile);
+ void updateQsTile(in Tile tile, in IBinder service);
+ void updateStatusIcon(in IBinder tile, in Icon icon,
String contentDescription);
- void onShowDialog(in Tile tile);
- void onStartActivity(in Tile tile);
+ void onShowDialog(in IBinder tile);
+ void onStartActivity(in IBinder tile);
boolean isLocked();
boolean isSecure();
- void startUnlockAndRun(in Tile tile);
+ void startUnlockAndRun(in IBinder tile);
- void onDialogHidden(in Tile tile);
- void onStartSuccessful(in Tile tile);
+ void onDialogHidden(in IBinder tile);
+ void onStartSuccessful(in IBinder tile);
}
diff --git a/core/java/android/service/quicksettings/Tile.java b/core/java/android/service/quicksettings/Tile.java
index 3d7d53e..4b81a72 100644
--- a/core/java/android/service/quicksettings/Tile.java
+++ b/core/java/android/service/quicksettings/Tile.java
@@ -15,8 +15,8 @@
*/
package android.service.quicksettings;
-import android.content.ComponentName;
import android.graphics.drawable.Icon;
+import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
@@ -59,7 +59,7 @@
*/
public static final int STATE_ACTIVE = 2;
- private ComponentName mComponentName;
+ private IBinder mToken;
private Icon mIcon;
private CharSequence mLabel;
private CharSequence mContentDescription;
@@ -78,29 +78,15 @@
/**
* @hide
*/
- public Tile(ComponentName componentName) {
- mComponentName = componentName;
+ public Tile() {
}
/**
* @hide
*/
- public void setService(IQSService service) {
+ public void setService(IQSService service, IBinder stub) {
mService = service;
- }
-
- /**
- * @hide
- */
- public ComponentName getComponentName() {
- return mComponentName;
- }
-
- /**
- * @hide
- */
- public IQSService getQsService() {
- return mService;
+ mToken = stub;
}
/**
@@ -193,7 +179,7 @@
*/
public void updateTile() {
try {
- mService.updateQsTile(this);
+ mService.updateQsTile(this, mToken);
} catch (RemoteException e) {
Log.e(TAG, "Couldn't update tile");
}
@@ -201,12 +187,6 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- if (mComponentName != null) {
- dest.writeByte((byte) 1);
- mComponentName.writeToParcel(dest, flags);
- } else {
- dest.writeByte((byte) 0);
- }
if (mIcon != null) {
dest.writeByte((byte) 1);
mIcon.writeToParcel(dest, flags);
@@ -220,11 +200,6 @@
private void readFromParcel(Parcel source) {
if (source.readByte() != 0) {
- mComponentName = ComponentName.CREATOR.createFromParcel(source);
- } else {
- mComponentName = null;
- }
- if (source.readByte() != 0) {
mIcon = Icon.CREATOR.createFromParcel(source);
} else {
mIcon = null;
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index 50411ab..887f4b6 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -123,6 +123,11 @@
/**
* @hide
*/
+ public static final String EXTRA_TOKEN = "token";
+
+ /**
+ * @hide
+ */
public static final String EXTRA_COMPONENT = "android.service.quicksettings.extra.COMPONENT";
private final H mHandler = new H(Looper.getMainLooper());
@@ -132,6 +137,7 @@
private IBinder mToken;
private IQSService mService;
private Runnable mUnlockRunnable;
+ private IBinder mTileToken;
@Override
public void onDestroy() {
@@ -197,7 +203,7 @@
public final void setStatusIcon(Icon icon, String contentDescription) {
if (mService != null) {
try {
- mService.updateStatusIcon(mTile, icon, contentDescription);
+ mService.updateStatusIcon(mTileToken, icon, contentDescription);
} catch (RemoteException e) {
}
}
@@ -224,14 +230,14 @@
@Override
public void onViewDetachedFromWindow(View v) {
try {
- mService.onDialogHidden(getQsTile());
+ mService.onDialogHidden(mTileToken);
} catch (RemoteException e) {
}
}
});
dialog.show();
try {
- mService.onShowDialog(mTile);
+ mService.onShowDialog(mTileToken);
} catch (RemoteException e) {
}
}
@@ -246,7 +252,7 @@
public final void unlockAndRun(Runnable runnable) {
mUnlockRunnable = runnable;
try {
- mService.startUnlockAndRun(mTile);
+ mService.startUnlockAndRun(mTileToken);
} catch (RemoteException e) {
}
}
@@ -292,7 +298,7 @@
public final void startActivityAndCollapse(Intent intent) {
startActivity(intent);
try {
- mService.onStartActivity(mTile);
+ mService.onStartActivity(mTileToken);
} catch (RemoteException e) {
}
}
@@ -311,14 +317,14 @@
@Override
public IBinder onBind(Intent intent) {
mService = IQSService.Stub.asInterface(intent.getIBinderExtra(EXTRA_SERVICE));
+ mTileToken = intent.getIBinderExtra(EXTRA_TOKEN);
try {
- ComponentName component = intent.getParcelableExtra(EXTRA_COMPONENT);
- mTile = mService.getTile(component);
+ mTile = mService.getTile(mTileToken);
} catch (RemoteException e) {
throw new RuntimeException("Unable to reach IQSService", e);
}
if (mTile != null) {
- mTile.setService(mService);
+ mTile.setService(mService, mTileToken);
mHandler.sendEmptyMessage(H.MSG_START_SUCCESS);
}
return new IQSTileService.Stub() {
@@ -403,7 +409,7 @@
break;
case MSG_START_SUCCESS:
try {
- mService.onStartSuccessful(mTile);
+ mService.onStartSuccessful(mTileToken);
} catch (RemoteException e) {
}
break;
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index de8133b..06d87f8 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -55,6 +55,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
import android.view.WindowManagerGlobal;
import java.io.FileDescriptor;
@@ -628,9 +629,9 @@
mCurWindowFlags = mWindowFlags;
mLayout.flags = mWindowFlags
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- ;
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mCurWindowPrivateFlags = mWindowPrivateFlags;
mLayout.privateFlags = mWindowPrivateFlags;
diff --git a/core/java/android/text/Emoji.java b/core/java/android/text/Emoji.java
index b62cc66..b6d720d 100644
--- a/core/java/android/text/Emoji.java
+++ b/core/java/android/text/Emoji.java
@@ -32,111 +32,112 @@
0x23EC, 0x23ED, 0x23EE, 0x23EF, 0x23F0, 0x23F1, 0x23F2, 0x23F3, 0x23F8, 0x23F9, 0x23FA,
0x24C2, 0x25AA, 0x25AB, 0x25B6, 0x25C0, 0x25FB, 0x25FC, 0x25FD, 0x25FE, 0x2600, 0x2601,
0x2602, 0x2603, 0x2604, 0x260E, 0x2611, 0x2614, 0x2615, 0x2618, 0x261D, 0x2620, 0x2622,
- 0x2623, 0x2626, 0x262A, 0x262E, 0x262F, 0x2638, 0x2639, 0x263A, 0x2648, 0x2649, 0x264A,
- 0x264B, 0x264C, 0x264D, 0x264E, 0x264F, 0x2650, 0x2651, 0x2652, 0x2653, 0x2660, 0x2663,
- 0x2665, 0x2666, 0x2668, 0x267B, 0x267F, 0x2692, 0x2693, 0x2694, 0x2696, 0x2697, 0x2699,
- 0x269B, 0x269C, 0x26A0, 0x26A1, 0x26AA, 0x26AB, 0x26B0, 0x26B1, 0x26BD, 0x26BE, 0x26C4,
- 0x26C5, 0x26C8, 0x26CE, 0x26CF, 0x26D1, 0x26D3, 0x26D4, 0x26E9, 0x26EA, 0x26F0, 0x26F1,
- 0x26F2, 0x26F3, 0x26F4, 0x26F5, 0x26F7, 0x26F8, 0x26F9, 0x26FA, 0x26FD, 0x2702, 0x2705,
- 0x2708, 0x2709, 0x270A, 0x270B, 0x270C, 0x270D, 0x270F, 0x2712, 0x2714, 0x2716, 0x271D,
- 0x2721, 0x2728, 0x2733, 0x2734, 0x2744, 0x2747, 0x274C, 0x274E, 0x2753, 0x2754, 0x2755,
- 0x2757, 0x2763, 0x2764, 0x2795, 0x2796, 0x2797, 0x27A1, 0x27B0, 0x27BF, 0x2934, 0x2935,
- 0x2B05, 0x2B06, 0x2B07, 0x2B1B, 0x2B1C, 0x2B50, 0x2B55, 0x3030, 0x303D, 0x3297, 0x3299,
- 0x1F004, 0x1F0CF, 0x1F170, 0x1F171, 0x1F17E, 0x1F17F, 0x1F18E, 0x1F191, 0x1F192, 0x1F193,
- 0x1F194, 0x1F195, 0x1F196, 0x1F197, 0x1F198, 0x1F199, 0x1F19A, 0x1F1E6, 0x1F1E7, 0x1F1E8,
- 0x1F1E9, 0x1F1EA, 0x1F1EB, 0x1F1EC, 0x1F1ED, 0x1F1EE, 0x1F1EF, 0x1F1F0, 0x1F1F1, 0x1F1F2,
- 0x1F1F3, 0x1F1F4, 0x1F1F5, 0x1F1F6, 0x1F1F7, 0x1F1F8, 0x1F1F9, 0x1F1FA, 0x1F1FB, 0x1F1FC,
- 0x1F1FD, 0x1F1FE, 0x1F1FF, 0x1F201, 0x1F202, 0x1F21A, 0x1F22F, 0x1F232, 0x1F233, 0x1F234,
- 0x1F235, 0x1F236, 0x1F237, 0x1F238, 0x1F239, 0x1F23A, 0x1F250, 0x1F251, 0x1F300, 0x1F301,
- 0x1F302, 0x1F303, 0x1F304, 0x1F305, 0x1F306, 0x1F307, 0x1F308, 0x1F309, 0x1F30A, 0x1F30B,
- 0x1F30C, 0x1F30D, 0x1F30E, 0x1F30F, 0x1F310, 0x1F311, 0x1F312, 0x1F313, 0x1F314, 0x1F315,
- 0x1F316, 0x1F317, 0x1F318, 0x1F319, 0x1F31A, 0x1F31B, 0x1F31C, 0x1F31D, 0x1F31E, 0x1F31F,
- 0x1F320, 0x1F321, 0x1F324, 0x1F325, 0x1F326, 0x1F327, 0x1F328, 0x1F329, 0x1F32A, 0x1F32B,
- 0x1F32C, 0x1F32D, 0x1F32E, 0x1F32F, 0x1F330, 0x1F331, 0x1F332, 0x1F333, 0x1F334, 0x1F335,
- 0x1F336, 0x1F337, 0x1F338, 0x1F339, 0x1F33A, 0x1F33B, 0x1F33C, 0x1F33D, 0x1F33E, 0x1F33F,
- 0x1F340, 0x1F341, 0x1F342, 0x1F343, 0x1F344, 0x1F345, 0x1F346, 0x1F347, 0x1F348, 0x1F349,
- 0x1F34A, 0x1F34B, 0x1F34C, 0x1F34D, 0x1F34E, 0x1F34F, 0x1F350, 0x1F351, 0x1F352, 0x1F353,
- 0x1F354, 0x1F355, 0x1F356, 0x1F357, 0x1F358, 0x1F359, 0x1F35A, 0x1F35B, 0x1F35C, 0x1F35D,
- 0x1F35E, 0x1F35F, 0x1F360, 0x1F361, 0x1F362, 0x1F363, 0x1F364, 0x1F365, 0x1F366, 0x1F367,
- 0x1F368, 0x1F369, 0x1F36A, 0x1F36B, 0x1F36C, 0x1F36D, 0x1F36E, 0x1F36F, 0x1F370, 0x1F371,
- 0x1F372, 0x1F373, 0x1F374, 0x1F375, 0x1F376, 0x1F377, 0x1F378, 0x1F379, 0x1F37A, 0x1F37B,
- 0x1F37C, 0x1F37D, 0x1F37E, 0x1F37F, 0x1F380, 0x1F381, 0x1F382, 0x1F383, 0x1F384, 0x1F385,
- 0x1F386, 0x1F387, 0x1F388, 0x1F389, 0x1F38A, 0x1F38B, 0x1F38C, 0x1F38D, 0x1F38E, 0x1F38F,
- 0x1F390, 0x1F391, 0x1F392, 0x1F393, 0x1F396, 0x1F397, 0x1F399, 0x1F39A, 0x1F39B, 0x1F39E,
- 0x1F39F, 0x1F3A0, 0x1F3A1, 0x1F3A2, 0x1F3A3, 0x1F3A4, 0x1F3A5, 0x1F3A6, 0x1F3A7, 0x1F3A8,
- 0x1F3A9, 0x1F3AA, 0x1F3AB, 0x1F3AC, 0x1F3AD, 0x1F3AE, 0x1F3AF, 0x1F3B0, 0x1F3B1, 0x1F3B2,
- 0x1F3B3, 0x1F3B4, 0x1F3B5, 0x1F3B6, 0x1F3B7, 0x1F3B8, 0x1F3B9, 0x1F3BA, 0x1F3BB, 0x1F3BC,
- 0x1F3BD, 0x1F3BE, 0x1F3BF, 0x1F3C0, 0x1F3C1, 0x1F3C2, 0x1F3C3, 0x1F3C4, 0x1F3C5, 0x1F3C6,
- 0x1F3C7, 0x1F3C8, 0x1F3C9, 0x1F3CA, 0x1F3CB, 0x1F3CC, 0x1F3CD, 0x1F3CE, 0x1F3CF, 0x1F3D0,
- 0x1F3D1, 0x1F3D2, 0x1F3D3, 0x1F3D4, 0x1F3D5, 0x1F3D6, 0x1F3D7, 0x1F3D8, 0x1F3D9, 0x1F3DA,
- 0x1F3DB, 0x1F3DC, 0x1F3DD, 0x1F3DE, 0x1F3DF, 0x1F3E0, 0x1F3E1, 0x1F3E2, 0x1F3E3, 0x1F3E4,
- 0x1F3E5, 0x1F3E6, 0x1F3E7, 0x1F3E8, 0x1F3E9, 0x1F3EA, 0x1F3EB, 0x1F3EC, 0x1F3ED, 0x1F3EE,
- 0x1F3EF, 0x1F3F0, 0x1F3F3, 0x1F3F4, 0x1F3F5, 0x1F3F7, 0x1F3F8, 0x1F3F9, 0x1F3FA, 0x1F3FB,
- 0x1F3FC, 0x1F3FD, 0x1F3FE, 0x1F3FF, 0x1F400, 0x1F401, 0x1F402, 0x1F403, 0x1F404, 0x1F405,
- 0x1F406, 0x1F407, 0x1F408, 0x1F409, 0x1F40A, 0x1F40B, 0x1F40C, 0x1F40D, 0x1F40E, 0x1F40F,
- 0x1F410, 0x1F411, 0x1F412, 0x1F413, 0x1F414, 0x1F415, 0x1F416, 0x1F417, 0x1F418, 0x1F419,
- 0x1F41A, 0x1F41B, 0x1F41C, 0x1F41D, 0x1F41E, 0x1F41F, 0x1F420, 0x1F421, 0x1F422, 0x1F423,
- 0x1F424, 0x1F425, 0x1F426, 0x1F427, 0x1F428, 0x1F429, 0x1F42A, 0x1F42B, 0x1F42C, 0x1F42D,
- 0x1F42E, 0x1F42F, 0x1F430, 0x1F431, 0x1F432, 0x1F433, 0x1F434, 0x1F435, 0x1F436, 0x1F437,
- 0x1F438, 0x1F439, 0x1F43A, 0x1F43B, 0x1F43C, 0x1F43D, 0x1F43E, 0x1F43F, 0x1F440, 0x1F441,
- 0x1F442, 0x1F443, 0x1F444, 0x1F445, 0x1F446, 0x1F447, 0x1F448, 0x1F449, 0x1F44A, 0x1F44B,
- 0x1F44C, 0x1F44D, 0x1F44E, 0x1F44F, 0x1F450, 0x1F451, 0x1F452, 0x1F453, 0x1F454, 0x1F455,
- 0x1F456, 0x1F457, 0x1F458, 0x1F459, 0x1F45A, 0x1F45B, 0x1F45C, 0x1F45D, 0x1F45E, 0x1F45F,
- 0x1F460, 0x1F461, 0x1F462, 0x1F463, 0x1F464, 0x1F465, 0x1F466, 0x1F467, 0x1F468, 0x1F469,
- 0x1F46A, 0x1F46B, 0x1F46C, 0x1F46D, 0x1F46E, 0x1F46F, 0x1F470, 0x1F471, 0x1F472, 0x1F473,
- 0x1F474, 0x1F475, 0x1F476, 0x1F477, 0x1F478, 0x1F479, 0x1F47A, 0x1F47B, 0x1F47C, 0x1F47D,
- 0x1F47E, 0x1F47F, 0x1F480, 0x1F481, 0x1F482, 0x1F483, 0x1F484, 0x1F485, 0x1F486, 0x1F487,
- 0x1F488, 0x1F489, 0x1F48A, 0x1F48B, 0x1F48C, 0x1F48D, 0x1F48E, 0x1F48F, 0x1F490, 0x1F491,
- 0x1F492, 0x1F493, 0x1F494, 0x1F495, 0x1F496, 0x1F497, 0x1F498, 0x1F499, 0x1F49A, 0x1F49B,
- 0x1F49C, 0x1F49D, 0x1F49E, 0x1F49F, 0x1F4A0, 0x1F4A1, 0x1F4A2, 0x1F4A3, 0x1F4A4, 0x1F4A5,
- 0x1F4A6, 0x1F4A7, 0x1F4A8, 0x1F4A9, 0x1F4AA, 0x1F4AB, 0x1F4AC, 0x1F4AD, 0x1F4AE, 0x1F4AF,
- 0x1F4B0, 0x1F4B1, 0x1F4B2, 0x1F4B3, 0x1F4B4, 0x1F4B5, 0x1F4B6, 0x1F4B7, 0x1F4B8, 0x1F4B9,
- 0x1F4BA, 0x1F4BB, 0x1F4BC, 0x1F4BD, 0x1F4BE, 0x1F4BF, 0x1F4C0, 0x1F4C1, 0x1F4C2, 0x1F4C3,
- 0x1F4C4, 0x1F4C5, 0x1F4C6, 0x1F4C7, 0x1F4C8, 0x1F4C9, 0x1F4CA, 0x1F4CB, 0x1F4CC, 0x1F4CD,
- 0x1F4CE, 0x1F4CF, 0x1F4D0, 0x1F4D1, 0x1F4D2, 0x1F4D3, 0x1F4D4, 0x1F4D5, 0x1F4D6, 0x1F4D7,
- 0x1F4D8, 0x1F4D9, 0x1F4DA, 0x1F4DB, 0x1F4DC, 0x1F4DD, 0x1F4DE, 0x1F4DF, 0x1F4E0, 0x1F4E1,
- 0x1F4E2, 0x1F4E3, 0x1F4E4, 0x1F4E5, 0x1F4E6, 0x1F4E7, 0x1F4E8, 0x1F4E9, 0x1F4EA, 0x1F4EB,
- 0x1F4EC, 0x1F4ED, 0x1F4EE, 0x1F4EF, 0x1F4F0, 0x1F4F1, 0x1F4F2, 0x1F4F3, 0x1F4F4, 0x1F4F5,
- 0x1F4F6, 0x1F4F7, 0x1F4F8, 0x1F4F9, 0x1F4FA, 0x1F4FB, 0x1F4FC, 0x1F4FD, 0x1F4FF, 0x1F500,
- 0x1F501, 0x1F502, 0x1F503, 0x1F504, 0x1F505, 0x1F506, 0x1F507, 0x1F508, 0x1F509, 0x1F50A,
- 0x1F50B, 0x1F50C, 0x1F50D, 0x1F50E, 0x1F50F, 0x1F510, 0x1F511, 0x1F512, 0x1F513, 0x1F514,
- 0x1F515, 0x1F516, 0x1F517, 0x1F518, 0x1F519, 0x1F51A, 0x1F51B, 0x1F51C, 0x1F51D, 0x1F51E,
- 0x1F51F, 0x1F520, 0x1F521, 0x1F522, 0x1F523, 0x1F524, 0x1F525, 0x1F526, 0x1F527, 0x1F528,
- 0x1F529, 0x1F52A, 0x1F52B, 0x1F52C, 0x1F52D, 0x1F52E, 0x1F52F, 0x1F530, 0x1F531, 0x1F532,
- 0x1F533, 0x1F534, 0x1F535, 0x1F536, 0x1F537, 0x1F538, 0x1F539, 0x1F53A, 0x1F53B, 0x1F53C,
- 0x1F53D, 0x1F549, 0x1F54A, 0x1F54B, 0x1F54C, 0x1F54D, 0x1F54E, 0x1F550, 0x1F551, 0x1F552,
- 0x1F553, 0x1F554, 0x1F555, 0x1F556, 0x1F557, 0x1F558, 0x1F559, 0x1F55A, 0x1F55B, 0x1F55C,
- 0x1F55D, 0x1F55E, 0x1F55F, 0x1F560, 0x1F561, 0x1F562, 0x1F563, 0x1F564, 0x1F565, 0x1F566,
- 0x1F567, 0x1F56F, 0x1F570, 0x1F573, 0x1F574, 0x1F575, 0x1F576, 0x1F577, 0x1F578, 0x1F579,
- 0x1F57A, 0x1F587, 0x1F58A, 0x1F58B, 0x1F58C, 0x1F58D, 0x1F590, 0x1F595, 0x1F596, 0x1F5A4,
- 0x1F5A5, 0x1F5A8, 0x1F5B1, 0x1F5B2, 0x1F5BC, 0x1F5C2, 0x1F5C3, 0x1F5C4, 0x1F5D1, 0x1F5D2,
- 0x1F5D3, 0x1F5DC, 0x1F5DD, 0x1F5DE, 0x1F5E1, 0x1F5E3, 0x1F5E8, 0x1F5EF, 0x1F5F3, 0x1F5FA,
- 0x1F5FB, 0x1F5FC, 0x1F5FD, 0x1F5FE, 0x1F5FF, 0x1F600, 0x1F601, 0x1F602, 0x1F603, 0x1F604,
- 0x1F605, 0x1F606, 0x1F607, 0x1F608, 0x1F609, 0x1F60A, 0x1F60B, 0x1F60C, 0x1F60D, 0x1F60E,
- 0x1F60F, 0x1F610, 0x1F611, 0x1F612, 0x1F613, 0x1F614, 0x1F615, 0x1F616, 0x1F617, 0x1F618,
- 0x1F619, 0x1F61A, 0x1F61B, 0x1F61C, 0x1F61D, 0x1F61E, 0x1F61F, 0x1F620, 0x1F621, 0x1F622,
- 0x1F623, 0x1F624, 0x1F625, 0x1F626, 0x1F627, 0x1F628, 0x1F629, 0x1F62A, 0x1F62B, 0x1F62C,
- 0x1F62D, 0x1F62E, 0x1F62F, 0x1F630, 0x1F631, 0x1F632, 0x1F633, 0x1F634, 0x1F635, 0x1F636,
- 0x1F637, 0x1F638, 0x1F639, 0x1F63A, 0x1F63B, 0x1F63C, 0x1F63D, 0x1F63E, 0x1F63F, 0x1F640,
- 0x1F641, 0x1F642, 0x1F643, 0x1F644, 0x1F645, 0x1F646, 0x1F647, 0x1F648, 0x1F649, 0x1F64A,
- 0x1F64B, 0x1F64C, 0x1F64D, 0x1F64E, 0x1F64F, 0x1F680, 0x1F681, 0x1F682, 0x1F683, 0x1F684,
- 0x1F685, 0x1F686, 0x1F687, 0x1F688, 0x1F689, 0x1F68A, 0x1F68B, 0x1F68C, 0x1F68D, 0x1F68E,
- 0x1F68F, 0x1F690, 0x1F691, 0x1F692, 0x1F693, 0x1F694, 0x1F695, 0x1F696, 0x1F697, 0x1F698,
- 0x1F699, 0x1F69A, 0x1F69B, 0x1F69C, 0x1F69D, 0x1F69E, 0x1F69F, 0x1F6A0, 0x1F6A1, 0x1F6A2,
- 0x1F6A3, 0x1F6A4, 0x1F6A5, 0x1F6A6, 0x1F6A7, 0x1F6A8, 0x1F6A9, 0x1F6AA, 0x1F6AB, 0x1F6AC,
- 0x1F6AD, 0x1F6AE, 0x1F6AF, 0x1F6B0, 0x1F6B1, 0x1F6B2, 0x1F6B3, 0x1F6B4, 0x1F6B5, 0x1F6B6,
- 0x1F6B7, 0x1F6B8, 0x1F6B9, 0x1F6BA, 0x1F6BB, 0x1F6BC, 0x1F6BD, 0x1F6BE, 0x1F6BF, 0x1F6C0,
- 0x1F6C1, 0x1F6C2, 0x1F6C3, 0x1F6C4, 0x1F6C5, 0x1F6CB, 0x1F6CC, 0x1F6CD, 0x1F6CE, 0x1F6CF,
- 0x1F6D0, 0x1F6D1, 0x1F6D2, 0x1F6E0, 0x1F6E1, 0x1F6E2, 0x1F6E3, 0x1F6E4, 0x1F6E5, 0x1F6E9,
- 0x1F6EB, 0x1F6EC, 0x1F6F0, 0x1F6F3, 0x1F6F4, 0x1F6F5, 0x1F6F6, 0x1F910, 0x1F911, 0x1F912,
- 0x1F913, 0x1F914, 0x1F915, 0x1F916, 0x1F917, 0x1F918, 0x1F919, 0x1F91A, 0x1F91B, 0x1F91C,
- 0x1F91D, 0x1F91E, 0x1F920, 0x1F921, 0x1F922, 0x1F923, 0x1F924, 0x1F925, 0x1F926, 0x1F927,
- 0x1F930, 0x1F933, 0x1F934, 0x1F935, 0x1F936, 0x1F937, 0x1F938, 0x1F939, 0x1F93A, 0x1F93B,
- 0x1F93C, 0x1F93D, 0x1F93E, 0x1F940, 0x1F941, 0x1F942, 0x1F943, 0x1F944, 0x1F945, 0x1F946,
- 0x1F947, 0x1F948, 0x1F949, 0x1F94A, 0x1F94B, 0x1F950, 0x1F951, 0x1F952, 0x1F953, 0x1F954,
- 0x1F955, 0x1F956, 0x1F957, 0x1F958, 0x1F959, 0x1F95A, 0x1F95B, 0x1F95C, 0x1F95D, 0x1F95E,
- 0x1F980, 0x1F981, 0x1F982, 0x1F983, 0x1F984, 0x1F985, 0x1F986, 0x1F987, 0x1F988, 0x1F989,
- 0x1F98A, 0x1F98B, 0x1F98C, 0x1F98D, 0x1F98E, 0x1F98F, 0x1F990, 0x1F991, 0x1F9C0
+ 0x2623, 0x2626, 0x262A, 0x262E, 0x262F, 0x2638, 0x2639, 0x263A, 0x2640, 0x2642, 0x2648,
+ 0x2649, 0x264A, 0x264B, 0x264C, 0x264D, 0x264E, 0x264F, 0x2650, 0x2651, 0x2652, 0x2653,
+ 0x2660, 0x2663, 0x2665, 0x2666, 0x2668, 0x267B, 0x267F, 0x2692, 0x2693, 0x2694, 0x2695,
+ 0x2696, 0x2697, 0x2699, 0x269B, 0x269C, 0x26A0, 0x26A1, 0x26AA, 0x26AB, 0x26B0, 0x26B1,
+ 0x26BD, 0x26BE, 0x26C4, 0x26C5, 0x26C8, 0x26CE, 0x26CF, 0x26D1, 0x26D3, 0x26D4, 0x26E9,
+ 0x26EA, 0x26F0, 0x26F1, 0x26F2, 0x26F3, 0x26F4, 0x26F5, 0x26F7, 0x26F8, 0x26F9, 0x26FA,
+ 0x26FD, 0x2702, 0x2705, 0x2708, 0x2709, 0x270A, 0x270B, 0x270C, 0x270D, 0x270F, 0x2712,
+ 0x2714, 0x2716, 0x271D, 0x2721, 0x2728, 0x2733, 0x2734, 0x2744, 0x2747, 0x274C, 0x274E,
+ 0x2753, 0x2754, 0x2755, 0x2757, 0x2763, 0x2764, 0x2795, 0x2796, 0x2797, 0x27A1, 0x27B0,
+ 0x27BF, 0x2934, 0x2935, 0x2B05, 0x2B06, 0x2B07, 0x2B1B, 0x2B1C, 0x2B50, 0x2B55, 0x3030,
+ 0x303D, 0x3297, 0x3299, 0x1F004, 0x1F0CF, 0x1F170, 0x1F171, 0x1F17E, 0x1F17F, 0x1F18E,
+ 0x1F191, 0x1F192, 0x1F193, 0x1F194, 0x1F195, 0x1F196, 0x1F197, 0x1F198, 0x1F199, 0x1F19A,
+ 0x1F1E6, 0x1F1E7, 0x1F1E8, 0x1F1E9, 0x1F1EA, 0x1F1EB, 0x1F1EC, 0x1F1ED, 0x1F1EE, 0x1F1EF,
+ 0x1F1F0, 0x1F1F1, 0x1F1F2, 0x1F1F3, 0x1F1F4, 0x1F1F5, 0x1F1F6, 0x1F1F7, 0x1F1F8, 0x1F1F9,
+ 0x1F1FA, 0x1F1FB, 0x1F1FC, 0x1F1FD, 0x1F1FE, 0x1F1FF, 0x1F201, 0x1F202, 0x1F21A, 0x1F22F,
+ 0x1F232, 0x1F233, 0x1F234, 0x1F235, 0x1F236, 0x1F237, 0x1F238, 0x1F239, 0x1F23A, 0x1F250,
+ 0x1F251, 0x1F300, 0x1F301, 0x1F302, 0x1F303, 0x1F304, 0x1F305, 0x1F306, 0x1F307, 0x1F308,
+ 0x1F309, 0x1F30A, 0x1F30B, 0x1F30C, 0x1F30D, 0x1F30E, 0x1F30F, 0x1F310, 0x1F311, 0x1F312,
+ 0x1F313, 0x1F314, 0x1F315, 0x1F316, 0x1F317, 0x1F318, 0x1F319, 0x1F31A, 0x1F31B, 0x1F31C,
+ 0x1F31D, 0x1F31E, 0x1F31F, 0x1F320, 0x1F321, 0x1F324, 0x1F325, 0x1F326, 0x1F327, 0x1F328,
+ 0x1F329, 0x1F32A, 0x1F32B, 0x1F32C, 0x1F32D, 0x1F32E, 0x1F32F, 0x1F330, 0x1F331, 0x1F332,
+ 0x1F333, 0x1F334, 0x1F335, 0x1F336, 0x1F337, 0x1F338, 0x1F339, 0x1F33A, 0x1F33B, 0x1F33C,
+ 0x1F33D, 0x1F33E, 0x1F33F, 0x1F340, 0x1F341, 0x1F342, 0x1F343, 0x1F344, 0x1F345, 0x1F346,
+ 0x1F347, 0x1F348, 0x1F349, 0x1F34A, 0x1F34B, 0x1F34C, 0x1F34D, 0x1F34E, 0x1F34F, 0x1F350,
+ 0x1F351, 0x1F352, 0x1F353, 0x1F354, 0x1F355, 0x1F356, 0x1F357, 0x1F358, 0x1F359, 0x1F35A,
+ 0x1F35B, 0x1F35C, 0x1F35D, 0x1F35E, 0x1F35F, 0x1F360, 0x1F361, 0x1F362, 0x1F363, 0x1F364,
+ 0x1F365, 0x1F366, 0x1F367, 0x1F368, 0x1F369, 0x1F36A, 0x1F36B, 0x1F36C, 0x1F36D, 0x1F36E,
+ 0x1F36F, 0x1F370, 0x1F371, 0x1F372, 0x1F373, 0x1F374, 0x1F375, 0x1F376, 0x1F377, 0x1F378,
+ 0x1F379, 0x1F37A, 0x1F37B, 0x1F37C, 0x1F37D, 0x1F37E, 0x1F37F, 0x1F380, 0x1F381, 0x1F382,
+ 0x1F383, 0x1F384, 0x1F385, 0x1F386, 0x1F387, 0x1F388, 0x1F389, 0x1F38A, 0x1F38B, 0x1F38C,
+ 0x1F38D, 0x1F38E, 0x1F38F, 0x1F390, 0x1F391, 0x1F392, 0x1F393, 0x1F396, 0x1F397, 0x1F399,
+ 0x1F39A, 0x1F39B, 0x1F39E, 0x1F39F, 0x1F3A0, 0x1F3A1, 0x1F3A2, 0x1F3A3, 0x1F3A4, 0x1F3A5,
+ 0x1F3A6, 0x1F3A7, 0x1F3A8, 0x1F3A9, 0x1F3AA, 0x1F3AB, 0x1F3AC, 0x1F3AD, 0x1F3AE, 0x1F3AF,
+ 0x1F3B0, 0x1F3B1, 0x1F3B2, 0x1F3B3, 0x1F3B4, 0x1F3B5, 0x1F3B6, 0x1F3B7, 0x1F3B8, 0x1F3B9,
+ 0x1F3BA, 0x1F3BB, 0x1F3BC, 0x1F3BD, 0x1F3BE, 0x1F3BF, 0x1F3C0, 0x1F3C1, 0x1F3C2, 0x1F3C3,
+ 0x1F3C4, 0x1F3C5, 0x1F3C6, 0x1F3C7, 0x1F3C8, 0x1F3C9, 0x1F3CA, 0x1F3CB, 0x1F3CC, 0x1F3CD,
+ 0x1F3CE, 0x1F3CF, 0x1F3D0, 0x1F3D1, 0x1F3D2, 0x1F3D3, 0x1F3D4, 0x1F3D5, 0x1F3D6, 0x1F3D7,
+ 0x1F3D8, 0x1F3D9, 0x1F3DA, 0x1F3DB, 0x1F3DC, 0x1F3DD, 0x1F3DE, 0x1F3DF, 0x1F3E0, 0x1F3E1,
+ 0x1F3E2, 0x1F3E3, 0x1F3E4, 0x1F3E5, 0x1F3E6, 0x1F3E7, 0x1F3E8, 0x1F3E9, 0x1F3EA, 0x1F3EB,
+ 0x1F3EC, 0x1F3ED, 0x1F3EE, 0x1F3EF, 0x1F3F0, 0x1F3F3, 0x1F3F4, 0x1F3F5, 0x1F3F7, 0x1F3F8,
+ 0x1F3F9, 0x1F3FA, 0x1F3FB, 0x1F3FC, 0x1F3FD, 0x1F3FE, 0x1F3FF, 0x1F400, 0x1F401, 0x1F402,
+ 0x1F403, 0x1F404, 0x1F405, 0x1F406, 0x1F407, 0x1F408, 0x1F409, 0x1F40A, 0x1F40B, 0x1F40C,
+ 0x1F40D, 0x1F40E, 0x1F40F, 0x1F410, 0x1F411, 0x1F412, 0x1F413, 0x1F414, 0x1F415, 0x1F416,
+ 0x1F417, 0x1F418, 0x1F419, 0x1F41A, 0x1F41B, 0x1F41C, 0x1F41D, 0x1F41E, 0x1F41F, 0x1F420,
+ 0x1F421, 0x1F422, 0x1F423, 0x1F424, 0x1F425, 0x1F426, 0x1F427, 0x1F428, 0x1F429, 0x1F42A,
+ 0x1F42B, 0x1F42C, 0x1F42D, 0x1F42E, 0x1F42F, 0x1F430, 0x1F431, 0x1F432, 0x1F433, 0x1F434,
+ 0x1F435, 0x1F436, 0x1F437, 0x1F438, 0x1F439, 0x1F43A, 0x1F43B, 0x1F43C, 0x1F43D, 0x1F43E,
+ 0x1F43F, 0x1F440, 0x1F441, 0x1F442, 0x1F443, 0x1F444, 0x1F445, 0x1F446, 0x1F447, 0x1F448,
+ 0x1F449, 0x1F44A, 0x1F44B, 0x1F44C, 0x1F44D, 0x1F44E, 0x1F44F, 0x1F450, 0x1F451, 0x1F452,
+ 0x1F453, 0x1F454, 0x1F455, 0x1F456, 0x1F457, 0x1F458, 0x1F459, 0x1F45A, 0x1F45B, 0x1F45C,
+ 0x1F45D, 0x1F45E, 0x1F45F, 0x1F460, 0x1F461, 0x1F462, 0x1F463, 0x1F464, 0x1F465, 0x1F466,
+ 0x1F467, 0x1F468, 0x1F469, 0x1F46A, 0x1F46B, 0x1F46C, 0x1F46D, 0x1F46E, 0x1F46F, 0x1F470,
+ 0x1F471, 0x1F472, 0x1F473, 0x1F474, 0x1F475, 0x1F476, 0x1F477, 0x1F478, 0x1F479, 0x1F47A,
+ 0x1F47B, 0x1F47C, 0x1F47D, 0x1F47E, 0x1F47F, 0x1F480, 0x1F481, 0x1F482, 0x1F483, 0x1F484,
+ 0x1F485, 0x1F486, 0x1F487, 0x1F488, 0x1F489, 0x1F48A, 0x1F48B, 0x1F48C, 0x1F48D, 0x1F48E,
+ 0x1F48F, 0x1F490, 0x1F491, 0x1F492, 0x1F493, 0x1F494, 0x1F495, 0x1F496, 0x1F497, 0x1F498,
+ 0x1F499, 0x1F49A, 0x1F49B, 0x1F49C, 0x1F49D, 0x1F49E, 0x1F49F, 0x1F4A0, 0x1F4A1, 0x1F4A2,
+ 0x1F4A3, 0x1F4A4, 0x1F4A5, 0x1F4A6, 0x1F4A7, 0x1F4A8, 0x1F4A9, 0x1F4AA, 0x1F4AB, 0x1F4AC,
+ 0x1F4AD, 0x1F4AE, 0x1F4AF, 0x1F4B0, 0x1F4B1, 0x1F4B2, 0x1F4B3, 0x1F4B4, 0x1F4B5, 0x1F4B6,
+ 0x1F4B7, 0x1F4B8, 0x1F4B9, 0x1F4BA, 0x1F4BB, 0x1F4BC, 0x1F4BD, 0x1F4BE, 0x1F4BF, 0x1F4C0,
+ 0x1F4C1, 0x1F4C2, 0x1F4C3, 0x1F4C4, 0x1F4C5, 0x1F4C6, 0x1F4C7, 0x1F4C8, 0x1F4C9, 0x1F4CA,
+ 0x1F4CB, 0x1F4CC, 0x1F4CD, 0x1F4CE, 0x1F4CF, 0x1F4D0, 0x1F4D1, 0x1F4D2, 0x1F4D3, 0x1F4D4,
+ 0x1F4D5, 0x1F4D6, 0x1F4D7, 0x1F4D8, 0x1F4D9, 0x1F4DA, 0x1F4DB, 0x1F4DC, 0x1F4DD, 0x1F4DE,
+ 0x1F4DF, 0x1F4E0, 0x1F4E1, 0x1F4E2, 0x1F4E3, 0x1F4E4, 0x1F4E5, 0x1F4E6, 0x1F4E7, 0x1F4E8,
+ 0x1F4E9, 0x1F4EA, 0x1F4EB, 0x1F4EC, 0x1F4ED, 0x1F4EE, 0x1F4EF, 0x1F4F0, 0x1F4F1, 0x1F4F2,
+ 0x1F4F3, 0x1F4F4, 0x1F4F5, 0x1F4F6, 0x1F4F7, 0x1F4F8, 0x1F4F9, 0x1F4FA, 0x1F4FB, 0x1F4FC,
+ 0x1F4FD, 0x1F4FF, 0x1F500, 0x1F501, 0x1F502, 0x1F503, 0x1F504, 0x1F505, 0x1F506, 0x1F507,
+ 0x1F508, 0x1F509, 0x1F50A, 0x1F50B, 0x1F50C, 0x1F50D, 0x1F50E, 0x1F50F, 0x1F510, 0x1F511,
+ 0x1F512, 0x1F513, 0x1F514, 0x1F515, 0x1F516, 0x1F517, 0x1F518, 0x1F519, 0x1F51A, 0x1F51B,
+ 0x1F51C, 0x1F51D, 0x1F51E, 0x1F51F, 0x1F520, 0x1F521, 0x1F522, 0x1F523, 0x1F524, 0x1F525,
+ 0x1F526, 0x1F527, 0x1F528, 0x1F529, 0x1F52A, 0x1F52B, 0x1F52C, 0x1F52D, 0x1F52E, 0x1F52F,
+ 0x1F530, 0x1F531, 0x1F532, 0x1F533, 0x1F534, 0x1F535, 0x1F536, 0x1F537, 0x1F538, 0x1F539,
+ 0x1F53A, 0x1F53B, 0x1F53C, 0x1F53D, 0x1F549, 0x1F54A, 0x1F54B, 0x1F54C, 0x1F54D, 0x1F54E,
+ 0x1F550, 0x1F551, 0x1F552, 0x1F553, 0x1F554, 0x1F555, 0x1F556, 0x1F557, 0x1F558, 0x1F559,
+ 0x1F55A, 0x1F55B, 0x1F55C, 0x1F55D, 0x1F55E, 0x1F55F, 0x1F560, 0x1F561, 0x1F562, 0x1F563,
+ 0x1F564, 0x1F565, 0x1F566, 0x1F567, 0x1F56F, 0x1F570, 0x1F573, 0x1F574, 0x1F575, 0x1F576,
+ 0x1F577, 0x1F578, 0x1F579, 0x1F57A, 0x1F587, 0x1F58A, 0x1F58B, 0x1F58C, 0x1F58D, 0x1F590,
+ 0x1F595, 0x1F596, 0x1F5A4, 0x1F5A5, 0x1F5A8, 0x1F5B1, 0x1F5B2, 0x1F5BC, 0x1F5C2, 0x1F5C3,
+ 0x1F5C4, 0x1F5D1, 0x1F5D2, 0x1F5D3, 0x1F5DC, 0x1F5DD, 0x1F5DE, 0x1F5E1, 0x1F5E3, 0x1F5E8,
+ 0x1F5EF, 0x1F5F3, 0x1F5FA, 0x1F5FB, 0x1F5FC, 0x1F5FD, 0x1F5FE, 0x1F5FF, 0x1F600, 0x1F601,
+ 0x1F602, 0x1F603, 0x1F604, 0x1F605, 0x1F606, 0x1F607, 0x1F608, 0x1F609, 0x1F60A, 0x1F60B,
+ 0x1F60C, 0x1F60D, 0x1F60E, 0x1F60F, 0x1F610, 0x1F611, 0x1F612, 0x1F613, 0x1F614, 0x1F615,
+ 0x1F616, 0x1F617, 0x1F618, 0x1F619, 0x1F61A, 0x1F61B, 0x1F61C, 0x1F61D, 0x1F61E, 0x1F61F,
+ 0x1F620, 0x1F621, 0x1F622, 0x1F623, 0x1F624, 0x1F625, 0x1F626, 0x1F627, 0x1F628, 0x1F629,
+ 0x1F62A, 0x1F62B, 0x1F62C, 0x1F62D, 0x1F62E, 0x1F62F, 0x1F630, 0x1F631, 0x1F632, 0x1F633,
+ 0x1F634, 0x1F635, 0x1F636, 0x1F637, 0x1F638, 0x1F639, 0x1F63A, 0x1F63B, 0x1F63C, 0x1F63D,
+ 0x1F63E, 0x1F63F, 0x1F640, 0x1F641, 0x1F642, 0x1F643, 0x1F644, 0x1F645, 0x1F646, 0x1F647,
+ 0x1F648, 0x1F649, 0x1F64A, 0x1F64B, 0x1F64C, 0x1F64D, 0x1F64E, 0x1F64F, 0x1F680, 0x1F681,
+ 0x1F682, 0x1F683, 0x1F684, 0x1F685, 0x1F686, 0x1F687, 0x1F688, 0x1F689, 0x1F68A, 0x1F68B,
+ 0x1F68C, 0x1F68D, 0x1F68E, 0x1F68F, 0x1F690, 0x1F691, 0x1F692, 0x1F693, 0x1F694, 0x1F695,
+ 0x1F696, 0x1F697, 0x1F698, 0x1F699, 0x1F69A, 0x1F69B, 0x1F69C, 0x1F69D, 0x1F69E, 0x1F69F,
+ 0x1F6A0, 0x1F6A1, 0x1F6A2, 0x1F6A3, 0x1F6A4, 0x1F6A5, 0x1F6A6, 0x1F6A7, 0x1F6A8, 0x1F6A9,
+ 0x1F6AA, 0x1F6AB, 0x1F6AC, 0x1F6AD, 0x1F6AE, 0x1F6AF, 0x1F6B0, 0x1F6B1, 0x1F6B2, 0x1F6B3,
+ 0x1F6B4, 0x1F6B5, 0x1F6B6, 0x1F6B7, 0x1F6B8, 0x1F6B9, 0x1F6BA, 0x1F6BB, 0x1F6BC, 0x1F6BD,
+ 0x1F6BE, 0x1F6BF, 0x1F6C0, 0x1F6C1, 0x1F6C2, 0x1F6C3, 0x1F6C4, 0x1F6C5, 0x1F6CB, 0x1F6CC,
+ 0x1F6CD, 0x1F6CE, 0x1F6CF, 0x1F6D0, 0x1F6D1, 0x1F6D2, 0x1F6E0, 0x1F6E1, 0x1F6E2, 0x1F6E3,
+ 0x1F6E4, 0x1F6E5, 0x1F6E9, 0x1F6EB, 0x1F6EC, 0x1F6F0, 0x1F6F3, 0x1F6F4, 0x1F6F5, 0x1F6F6,
+ 0x1F910, 0x1F911, 0x1F912, 0x1F913, 0x1F914, 0x1F915, 0x1F916, 0x1F917, 0x1F918, 0x1F919,
+ 0x1F91A, 0x1F91B, 0x1F91C, 0x1F91D, 0x1F91E, 0x1F920, 0x1F921, 0x1F922, 0x1F923, 0x1F924,
+ 0x1F925, 0x1F926, 0x1F927, 0x1F930, 0x1F933, 0x1F934, 0x1F935, 0x1F936, 0x1F937, 0x1F938,
+ 0x1F939, 0x1F93A, 0x1F93B, 0x1F93C, 0x1F93D, 0x1F93E, 0x1F940, 0x1F941, 0x1F942, 0x1F943,
+ 0x1F944, 0x1F945, 0x1F946, 0x1F947, 0x1F948, 0x1F949, 0x1F94A, 0x1F94B, 0x1F950, 0x1F951,
+ 0x1F952, 0x1F953, 0x1F954, 0x1F955, 0x1F956, 0x1F957, 0x1F958, 0x1F959, 0x1F95A, 0x1F95B,
+ 0x1F95C, 0x1F95D, 0x1F95E, 0x1F980, 0x1F981, 0x1F982, 0x1F983, 0x1F984, 0x1F985, 0x1F986,
+ 0x1F987, 0x1F988, 0x1F989, 0x1F98A, 0x1F98B, 0x1F98C, 0x1F98D, 0x1F98E, 0x1F98F, 0x1F990,
+ 0x1F991, 0x1F9C0
};
// See http://www.unicode.org/Public/emoji/3.0/emoji-data.txt
diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java
index 47e71be..4b02df86 100644
--- a/core/java/android/text/SpannableStringInternal.java
+++ b/core/java/android/text/SpannableStringInternal.java
@@ -33,6 +33,7 @@
mText = source.toString().substring(start, end);
mSpans = EmptyArray.OBJECT;
+ // Invariant: mSpanData.length = mSpans.length * COLUMNS
mSpanData = EmptyArray.INT;
if (source instanceof Spanned) {
@@ -99,7 +100,7 @@
Object[] srcSpans = src.mSpans;
mSpanCount = count;
mSpans = ArrayUtils.newUnpaddedObjectArray(mSpanCount);
- mSpanData = new int[mSpanCount * COLUMNS];
+ mSpanData = new int[mSpans.length * COLUMNS];
for (int i = 0, j = 0; i < limit; i++) {
int spanStart = srcData[i * COLUMNS + START];
int spanEnd = srcData[i * COLUMNS + END];
diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java
index 3770a45..2f327f3 100644
--- a/core/java/android/text/method/BaseKeyListener.java
+++ b/core/java/android/text/method/BaseKeyListener.java
@@ -129,8 +129,8 @@
// The offset is immediately before a variation selector.
final int STATE_BEFORE_VS = 6;
- // The offset is immediately before a ZWJ emoji.
- final int STATE_BEFORE_ZWJ_EMOJI = 7;
+ // The offset is immediately before an emoji.
+ final int STATE_BEFORE_EMOJI = 7;
// The offset is immediately before a ZWJ that were seen before a ZWJ emoji.
final int STATE_BEFORE_ZWJ = 8;
// The offset is immediately before a variation selector and a ZWJ that were seen before a
@@ -169,7 +169,7 @@
} else if (codePoint == Emoji.COMBINING_ENCLOSING_KEYCAP) {
state = STATE_BEFORE_KEYCAP;
} else if (Emoji.isEmoji(codePoint)) {
- state = STATE_BEFORE_ZWJ_EMOJI;
+ state = STATE_BEFORE_EMOJI;
} else {
state = STATE_FINISHED;
}
@@ -232,7 +232,7 @@
case STATE_BEFORE_VS:
if (Emoji.isEmoji(codePoint)) {
deleteCharCount += Character.charCount(codePoint);
- state = STATE_BEFORE_ZWJ_EMOJI;
+ state = STATE_BEFORE_EMOJI;
break;
}
@@ -242,7 +242,7 @@
}
state = STATE_FINISHED;
break;
- case STATE_BEFORE_ZWJ_EMOJI:
+ case STATE_BEFORE_EMOJI:
if (codePoint == Emoji.ZERO_WIDTH_JOINER) {
state = STATE_BEFORE_ZWJ;
} else {
@@ -252,7 +252,8 @@
case STATE_BEFORE_ZWJ:
if (Emoji.isEmoji(codePoint)) {
deleteCharCount += Character.charCount(codePoint) + 1; // +1 for ZWJ.
- state = STATE_BEFORE_ZWJ_EMOJI;
+ state = Emoji.isEmojiModifier(codePoint) ?
+ STATE_BEFORE_EMOJI_MODIFIER : STATE_BEFORE_EMOJI;
} else if (isVariationSelector(codePoint)) {
lastSeenVSCharCount = Character.charCount(codePoint);
state = STATE_BEFORE_VS_AND_ZWJ;
@@ -265,7 +266,7 @@
// +1 for ZWJ.
deleteCharCount += lastSeenVSCharCount + 1 + Character.charCount(codePoint);
lastSeenVSCharCount = 0;
- state = STATE_BEFORE_ZWJ_EMOJI;
+ state = STATE_BEFORE_EMOJI;
} else {
state = STATE_FINISHED;
}
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index 316c7e3..8823605 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -1941,6 +1941,26 @@
}
/**
+ * Force the transition to move to its end state, ending all the animators.
+ *
+ * @hide
+ */
+ void forceToEnd(ViewGroup sceneRoot) {
+ ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
+ int numOldAnims = runningAnimators.size();
+ if (sceneRoot != null) {
+ WindowId windowId = sceneRoot.getWindowId();
+ for (int i = numOldAnims - 1; i >= 0; i--) {
+ AnimationInfo info = runningAnimators.valueAt(i);
+ if (info.view != null && windowId != null && windowId.equals(info.windowId)) {
+ Animator anim = runningAnimators.keyAt(i);
+ anim.end();
+ }
+ }
+ }
+ }
+
+ /**
* This method cancels a transition that is currently running.
*
* @hide
diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java
index 71c8099..f2c871e3 100644
--- a/core/java/android/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -440,7 +440,7 @@
ArrayList<Transition> copy = new ArrayList(runningTransitions);
for (int i = copy.size() - 1; i >= 0; i--) {
final Transition transition = copy.get(i);
- transition.end();
+ transition.forceToEnd(sceneRoot);
}
}
diff --git a/core/java/android/transition/TransitionSet.java b/core/java/android/transition/TransitionSet.java
index 583dc0f..a41fe64 100644
--- a/core/java/android/transition/TransitionSet.java
+++ b/core/java/android/transition/TransitionSet.java
@@ -16,8 +16,6 @@
package android.transition;
-import com.android.internal.R;
-
import android.animation.TimeInterpolator;
import android.content.Context;
import android.content.res.TypedArray;
@@ -26,6 +24,8 @@
import android.view.View;
import android.view.ViewGroup;
+import com.android.internal.R;
+
import java.util.ArrayList;
/**
@@ -498,6 +498,16 @@
}
}
+ /** @hide */
+ @Override
+ void forceToEnd(ViewGroup sceneRoot) {
+ super.forceToEnd(sceneRoot);
+ int numTransitions = mTransitions.size();
+ for (int i = 0; i < numTransitions; ++i) {
+ mTransitions.get(i).forceToEnd(sceneRoot);
+ }
+ }
+
@Override
TransitionSet setSceneRoot(ViewGroup sceneRoot) {
super.setSceneRoot(sceneRoot);
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index d201ade..f4db4d6 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -66,9 +66,23 @@
* {@link #DENSITY_XHIGH} (320dpi). This is not a density that applications should target,
* instead relying on the system to scale their {@link #DENSITY_XHIGH} assets for them.
*/
+ public static final int DENSITY_260 = 260;
+
+ /**
+ * Intermediate density for screens that sit between {@link #DENSITY_HIGH} (240dpi) and
+ * {@link #DENSITY_XHIGH} (320dpi). This is not a density that applications should target,
+ * instead relying on the system to scale their {@link #DENSITY_XHIGH} assets for them.
+ */
public static final int DENSITY_280 = 280;
/**
+ * Intermediate density for screens that sit between {@link #DENSITY_HIGH} (240dpi) and
+ * {@link #DENSITY_XHIGH} (320dpi). This is not a density that applications should target,
+ * instead relying on the system to scale their {@link #DENSITY_XHIGH} assets for them.
+ */
+ public static final int DENSITY_300 = 300;
+
+ /**
* Standard quantized DPI for extra-high-density screens.
*/
public static final int DENSITY_XHIGH = 320;
@@ -79,6 +93,14 @@
* This is not a density that applications should target, instead relying
* on the system to scale their {@link #DENSITY_XXHIGH} assets for them.
*/
+ public static final int DENSITY_340 = 340;
+
+ /**
+ * Intermediate density for screens that sit somewhere between
+ * {@link #DENSITY_XHIGH} (320 dpi) and {@link #DENSITY_XXHIGH} (480 dpi).
+ * This is not a density that applications should target, instead relying
+ * on the system to scale their {@link #DENSITY_XXHIGH} assets for them.
+ */
public static final int DENSITY_360 = 360;
/**
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index d3db74d..3316f3a 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -25,6 +25,7 @@
import android.os.Trace;
import android.util.Log;
import android.util.TimeUtils;
+import android.view.animation.AnimationUtils;
import java.io.PrintWriter;
@@ -608,6 +609,7 @@
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
+ AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
@@ -620,6 +622,7 @@
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
} finally {
+ AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 2f2fe57..85a4bf9 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -36,7 +36,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
-import static android.Manifest.permission.CONFIGURE_DISPLAY_COLOR_TRANSFORM;
+import static android.Manifest.permission.CONFIGURE_DISPLAY_COLOR_MODE;
/**
* Provides information about the size and density of a logical display.
@@ -284,6 +284,27 @@
*/
public static final int STATE_DOZE_SUSPEND = 4;
+ /* The color mode constants defined below must be kept in sync with the ones in
+ * system/graphics.h */
+
+ /**
+ * Display color mode: The current color mode is unknown or invalid.
+ * @hide
+ */
+ public static final int COLOR_MODE_INVALID = -1;
+
+ /**
+ * Display color mode: The default or native gamut of the display.
+ * @hide
+ */
+ public static final int COLOR_MODE_DEFAULT = 0;
+
+ /**
+ * Display color mode: SRGB
+ * @hide
+ */
+ public static final int COLOR_MODE_SRGB = 7;
+
/**
* Internal method to create a display.
* Applications should use {@link android.view.WindowManager#getDefaultDisplay()}
@@ -463,20 +484,29 @@
/**
* Gets the size of the display, in pixels.
+ * Value returned by this method does not necessarily represent the actual raw size
+ * (native resolution) of the display.
* <p>
- * Note that this value should <em>not</em> be used for computing layouts,
- * since a device will typically have screen decoration (such as a status bar)
- * along the edges of the display that reduce the amount of application
- * space available from the size returned here. Layouts should instead use
- * the window size.
+ * 1. The returned size may be adjusted to exclude certain system decor elements
+ * that are always visible.
* </p><p>
- * The size is adjusted based on the current rotation of the display.
- * </p><p>
- * The size returned by this method does not necessarily represent the
- * actual raw size (native resolution) of the display. The returned size may
- * be adjusted to exclude certain system decoration elements that are always visible.
- * It may also be scaled to provide compatibility with older applications that
+ * 2. It may be scaled to provide compatibility with older applications that
* were originally designed for smaller displays.
+ * </p><p>
+ * 3. It can be different depending on the WindowManager to which the display belongs.
+ * </p><p>
+ * - If requested from non-Activity context (e.g. Application context via
+ * {@code (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE)})
+ * it will report the size of the entire display based on current rotation and with subtracted
+ * system decoration areas.
+ * </p><p>
+ * - If requested from activity (either using {@code getWindowManager()} or
+ * {@code (WindowManager) getSystemService(Context.WINDOW_SERVICE)}) resulting size will
+ * correspond to current app window size. In this case it can be smaller than physical size in
+ * multi-window mode.
+ * </p><p>
+ * Typically for the purposes of layout apps should make a request from activity context
+ * to obtain size available for the app content.
* </p>
*
* @param outSize A {@link Point} object to receive the size information.
@@ -687,33 +717,22 @@
}
/**
- * Request the display applies a color transform.
+ * Request the display applies a color mode.
* @hide
*/
- @RequiresPermission(CONFIGURE_DISPLAY_COLOR_TRANSFORM)
- public void requestColorTransform(ColorTransform colorTransform) {
- mGlobal.requestColorTransform(mDisplayId, colorTransform.getId());
+ @RequiresPermission(CONFIGURE_DISPLAY_COLOR_MODE)
+ public void requestColorMode(int colorMode) {
+ mGlobal.requestColorMode(mDisplayId, colorMode);
}
/**
- * Returns the active color transform of this display
+ * Returns the active color mode of this display
* @hide
*/
- public ColorTransform getColorTransform() {
+ public int getColorMode() {
synchronized (this) {
updateDisplayInfoLocked();
- return mDisplayInfo.getColorTransform();
- }
- }
-
- /**
- * Returns the default color transform of this display
- * @hide
- */
- public ColorTransform getDefaultColorTransform() {
- synchronized (this) {
- updateDisplayInfoLocked();
- return mDisplayInfo.getDefaultColorTransform();
+ return mDisplayInfo.colorMode;
}
}
@@ -728,14 +747,14 @@
}
/**
- * Gets the supported color transforms of this device.
+ * Gets the supported color modes of this device.
* @hide
*/
- public ColorTransform[] getSupportedColorTransforms() {
+ public int[] getSupportedColorModes() {
synchronized (this) {
updateDisplayInfoLocked();
- ColorTransform[] transforms = mDisplayInfo.supportedColorTransforms;
- return Arrays.copyOf(transforms, transforms.length);
+ int[] colorModes = mDisplayInfo.supportedColorModes;
+ return Arrays.copyOf(colorModes, colorModes.length);
}
}
@@ -785,13 +804,16 @@
* were originally designed for smaller displays.
* </p><p>
* 3. It can be different depending on the WindowManager to which the display belongs.
- * <pre>
+ * </p><p>
* - If requested from non-Activity context (e.g. Application context via
* {@code (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE)})
- * metrics will report real size of the display based on current rotation.
- * - If requested from activity resulting metrics will correspond to current window metrics.
- * In this case the size can be smaller than physical size in multi-window mode.
- * </pre>
+ * metrics will report the size of the entire display based on current rotation and with
+ * subtracted system decoration areas.
+ * </p><p>
+ * - If requested from activity (either using {@code getWindowManager()} or
+ * {@code (WindowManager) getSystemService(Context.WINDOW_SERVICE)}) resulting metrics will
+ * correspond to current app window metrics. In this case the size can be smaller than physical
+ * size in multi-window mode.
* </p>
*
* @param outMetrics A {@link DisplayMetrics} object to receive the metrics.
@@ -1205,6 +1227,33 @@
return mMinLuminance;
}
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (!(other instanceof HdrCapabilities)) {
+ return false;
+ }
+ HdrCapabilities that = (HdrCapabilities) other;
+
+ return Arrays.equals(mSupportedHdrTypes, that.mSupportedHdrTypes)
+ && mMaxLuminance == that.mMaxLuminance
+ && mMaxAverageLuminance == that.mMaxAverageLuminance
+ && mMinLuminance == that.mMinLuminance;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 23;
+ hash = hash * 17 + Arrays.hashCode(mSupportedHdrTypes);
+ hash = hash * 17 + Float.floatToIntBits(mMaxLuminance);
+ hash = hash * 17 + Float.floatToIntBits(mMaxAverageLuminance);
+ hash = hash * 17 + Float.floatToIntBits(mMinLuminance);
+ return hash;
+ }
+
public static final Creator<HdrCapabilities> CREATOR = new Creator<HdrCapabilities>() {
@Override
public HdrCapabilities createFromParcel(Parcel source) {
@@ -1251,89 +1300,4 @@
return 0;
}
}
-
- /**
- * A color transform supported by a given display.
- *
- * @see Display#getSupportedColorTransforms()
- * @hide
- */
- public static final class ColorTransform implements Parcelable {
- public static final ColorTransform[] EMPTY_ARRAY = new ColorTransform[0];
-
- private final int mId;
- private final int mColorTransform;
-
- public ColorTransform(int id, int colorTransform) {
- mId = id;
- mColorTransform = colorTransform;
- }
-
- public int getId() {
- return mId;
- }
-
- public int getColorTransform() {
- return mColorTransform;
- }
-
- @Override
- public boolean equals(Object other) {
- if (this == other) {
- return true;
- }
- if (!(other instanceof ColorTransform)) {
- return false;
- }
- ColorTransform that = (ColorTransform) other;
- return mId == that.mId
- && mColorTransform == that.mColorTransform;
- }
-
- @Override
- public int hashCode() {
- int hash = 1;
- hash = hash * 17 + mId;
- hash = hash * 17 + mColorTransform;
- return hash;
- }
-
- @Override
- public String toString() {
- return new StringBuilder("{")
- .append("id=").append(mId)
- .append(", colorTransform=").append(mColorTransform)
- .append("}")
- .toString();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- private ColorTransform(Parcel in) {
- this(in.readInt(), in.readInt());
- }
-
- @Override
- public void writeToParcel(Parcel out, int parcelableFlags) {
- out.writeInt(mId);
- out.writeInt(mColorTransform);
- }
-
- @SuppressWarnings("hiding")
- public static final Parcelable.Creator<ColorTransform> CREATOR
- = new Parcelable.Creator<ColorTransform>() {
- @Override
- public ColorTransform createFromParcel(Parcel in) {
- return new ColorTransform(in);
- }
-
- @Override
- public ColorTransform[] newArray(int size) {
- return new ColorTransform[size];
- }
- };
- }
}
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 8aeeffd..bc40849 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -169,14 +169,11 @@
*/
public Display.Mode[] supportedModes = Display.Mode.EMPTY_ARRAY;
- /** The active color transform. */
- public int colorTransformId;
+ /** The active color mode. */
+ public int colorMode;
- /** The default color transform. */
- public int defaultColorTransformId;
-
- /** The list of supported color transforms */
- public Display.ColorTransform[] supportedColorTransforms = Display.ColorTransform.EMPTY_ARRAY;
+ /** The list of supported color modes */
+ public int[] supportedColorModes = { Display.COLOR_MODE_DEFAULT };
/** The display's HDR capabilities */
public Display.HdrCapabilities hdrCapabilities;
@@ -291,8 +288,8 @@
&& rotation == other.rotation
&& modeId == other.modeId
&& defaultModeId == other.defaultModeId
- && colorTransformId == other.colorTransformId
- && defaultColorTransformId == other.defaultColorTransformId
+ && colorMode == other.colorMode
+ && Arrays.equals(supportedColorModes, other.supportedColorModes)
&& Objects.equal(hdrCapabilities, other.hdrCapabilities)
&& logicalDensityDpi == other.logicalDensityDpi
&& physicalXDpi == other.physicalXDpi
@@ -332,10 +329,9 @@
modeId = other.modeId;
defaultModeId = other.defaultModeId;
supportedModes = Arrays.copyOf(other.supportedModes, other.supportedModes.length);
- colorTransformId = other.colorTransformId;
- defaultColorTransformId = other.defaultColorTransformId;
- supportedColorTransforms = Arrays.copyOf(
- other.supportedColorTransforms, other.supportedColorTransforms.length);
+ colorMode = other.colorMode;
+ supportedColorModes = Arrays.copyOf(
+ other.supportedColorModes, other.supportedColorModes.length);
hdrCapabilities = other.hdrCapabilities;
logicalDensityDpi = other.logicalDensityDpi;
physicalXDpi = other.physicalXDpi;
@@ -373,12 +369,11 @@
for (int i = 0; i < nModes; i++) {
supportedModes[i] = Display.Mode.CREATOR.createFromParcel(source);
}
- colorTransformId = source.readInt();
- defaultColorTransformId = source.readInt();
- int nColorTransforms = source.readInt();
- supportedColorTransforms = new Display.ColorTransform[nColorTransforms];
- for (int i = 0; i < nColorTransforms; i++) {
- supportedColorTransforms[i] = Display.ColorTransform.CREATOR.createFromParcel(source);
+ colorMode = source.readInt();
+ int nColorModes = source.readInt();
+ supportedColorModes = new int[nColorModes];
+ for (int i = 0; i < nColorModes; i++) {
+ supportedColorModes[i] = source.readInt();
}
hdrCapabilities = source.readParcelable(null);
logicalDensityDpi = source.readInt();
@@ -418,11 +413,10 @@
for (int i = 0; i < supportedModes.length; i++) {
supportedModes[i].writeToParcel(dest, flags);
}
- dest.writeInt(colorTransformId);
- dest.writeInt(defaultColorTransformId);
- dest.writeInt(supportedColorTransforms.length);
- for (int i = 0; i < supportedColorTransforms.length; i++) {
- supportedColorTransforms[i].writeToParcel(dest, flags);
+ dest.writeInt(colorMode);
+ dest.writeInt(supportedColorModes.length);
+ for (int i = 0; i < supportedColorModes.length; i++) {
+ dest.writeInt(supportedColorModes[i]);
}
dest.writeParcelable(hdrCapabilities, flags);
dest.writeInt(logicalDensityDpi);
@@ -496,24 +490,6 @@
return result;
}
- public Display.ColorTransform getColorTransform() {
- return findColorTransform(colorTransformId);
- }
-
- public Display.ColorTransform getDefaultColorTransform() {
- return findColorTransform(defaultColorTransformId);
- }
-
- private Display.ColorTransform findColorTransform(int colorTransformId) {
- for (int i = 0; i < supportedColorTransforms.length; i++) {
- Display.ColorTransform colorTransform = supportedColorTransforms[i];
- if (colorTransform.getId() == colorTransformId) {
- return colorTransform;
- }
- }
- throw new IllegalStateException("Unable to locate color transform: " + colorTransformId);
- }
-
public void getAppMetrics(DisplayMetrics outMetrics) {
getAppMetrics(outMetrics, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
}
@@ -615,12 +591,10 @@
sb.append(defaultModeId);
sb.append(", modes ");
sb.append(Arrays.toString(supportedModes));
- sb.append(", colorTransformId ");
- sb.append(colorTransformId);
- sb.append(", defaultColorTransformId ");
- sb.append(defaultColorTransformId);
- sb.append(", supportedColorTransforms ");
- sb.append(Arrays.toString(supportedColorTransforms));
+ sb.append(", colorMode ");
+ sb.append(colorMode);
+ sb.append(", supportedColorModes ");
+ sb.append(Arrays.toString(supportedColorModes));
sb.append(", hdrCapabilities ");
sb.append(hdrCapabilities);
sb.append(", rotation ");
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
index 5c4450a..2b938d0 100644
--- a/core/java/android/view/FrameMetrics.java
+++ b/core/java/android/view/FrameMetrics.java
@@ -198,7 +198,7 @@
int SWAP_BUFFERS = 12;
int FRAME_COMPLETED = 13;
- int FRAME_STATS_COUNT = 14; // must always be last
+ int FRAME_STATS_COUNT = 16; // must always be last
}
/*
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 72126d0..81469c8 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -112,7 +112,7 @@
int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,
int configChanges, boolean voiceInteraction, boolean launchTaskBehind,
in Rect taskBounds, in Configuration configuration, int taskResizeMode,
- boolean alwaysFocusable, boolean homeTask, int targetSdkVersion);
+ boolean alwaysFocusable, boolean homeTask, int targetSdkVersion, int rotationAnimationHint);
/**
*
* @param token The token we are adding to the input task Id.
@@ -173,7 +173,8 @@
in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
int icon, int logo, int windowFlags, IBinder transferFrom, boolean createIfNeeded);
void setAppVisibility(IBinder token, boolean visible);
- void notifyAppStopped(IBinder token, boolean stopped);
+ void notifyAppResumed(IBinder token, boolean wasStopped, boolean allowSavedSurface);
+ void notifyAppStopped(IBinder token);
void startAppFreezingScreen(IBinder token, int configChanges);
void stopAppFreezingScreen(IBinder token, boolean force);
void removeAppToken(IBinder token);
@@ -306,6 +307,11 @@
boolean isRotationFrozen();
/**
+ * Screenshot the current wallpaper layer, including the whole screen.
+ */
+ Bitmap screenshotWallpaper();
+
+ /**
* Used only for assist -- request a screenshot of the current application.
*/
boolean requestAssistScreenshot(IAssistScreenshotReceiver receiver);
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 636384c..990d553 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -796,8 +796,16 @@
public static final int KEYCODE_COPY = 278;
/** Key code constant: Paste key. */
public static final int KEYCODE_PASTE = 279;
+ /** Key code constant: Consumed by the system for navigation up */
+ public static final int KEYCODE_SYSTEM_NAVIGATION_UP = 280;
+ /** Key code constant: Consumed by the system for navigation down */
+ public static final int KEYCODE_SYSTEM_NAVIGATION_DOWN = 281;
+ /** Key code constant: Consumed by the system for navigation left*/
+ public static final int KEYCODE_SYSTEM_NAVIGATION_LEFT = 282;
+ /** Key code constant: Consumed by the system for navigation right */
+ public static final int KEYCODE_SYSTEM_NAVIGATION_RIGHT = 283;
- private static final int LAST_KEYCODE = KEYCODE_PASTE;
+ private static final int LAST_KEYCODE = KEYCODE_SYSTEM_NAVIGATION_RIGHT;
// NOTE: If you add a new keycode here you must also add it to:
// isSystem()
@@ -1844,6 +1852,10 @@
case KeyEvent.KEYCODE_BRIGHTNESS_DOWN:
case KeyEvent.KEYCODE_BRIGHTNESS_UP:
case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
+ case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP:
+ case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN:
+ case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT:
+ case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT:
return true;
}
@@ -2929,11 +2941,13 @@
public static final Parcelable.Creator<KeyEvent> CREATOR
= new Parcelable.Creator<KeyEvent>() {
+ @Override
public KeyEvent createFromParcel(Parcel in) {
in.readInt(); // skip token, we already know this is a KeyEvent
return KeyEvent.createFromParcelBody(in);
}
+ @Override
public KeyEvent[] newArray(int size) {
return new KeyEvent[size];
}
@@ -2957,6 +2971,7 @@
mEventTime = in.readLong();
}
+ @Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(PARCEL_TOKEN_KEY_EVENT);
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index ab4cbcf..0164fcd 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -793,12 +793,12 @@
return mOwningView != null && mOwningView.mAttachInfo != null;
}
- public void addAnimator(AnimatedVectorDrawable.VectorDrawableAnimatorRT animatorSet) {
+ public void registerVectorDrawableAnimator(
+ AnimatedVectorDrawable.VectorDrawableAnimatorRT animatorSet) {
if (mOwningView == null || mOwningView.mAttachInfo == null) {
throw new IllegalStateException("Cannot start this animator on a detached view!");
}
- nAddAnimator(mNativeRenderNode, animatorSet.getAnimatorNativePtr());
- mOwningView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(this);
+ mOwningView.mAttachInfo.mViewRootImpl.registerVectorDrawableAnimator(animatorSet);
}
public void endAllAnimators() {
diff --git a/core/java/android/view/RoundScrollbarRenderer.java b/core/java/android/view/RoundScrollbarRenderer.java
new file mode 100644
index 0000000..b77be8c
--- /dev/null
+++ b/core/java/android/view/RoundScrollbarRenderer.java
@@ -0,0 +1,123 @@
+/*
+ * 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 android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.graphics.Rect;
+
+/**
+ * Helper class for drawing round scroll bars on round Wear devices.
+ */
+class RoundScrollbarRenderer {
+ // The range of the scrollbar position represented as an angle in degrees.
+ private static final int SCROLLBAR_ANGLE_RANGE = 90;
+ private static final int MAX_SCROLLBAR_ANGLE_SWIPE = 16;
+ private static final int MIN_SCROLLBAR_ANGLE_SWIPE = 6;
+ private static final float WIDTH_PERCENTAGE = 0.02f;
+ private static final int DEFAULT_THUMB_COLOR = 0xFF757575;
+ private static final int DEFAULT_TRACK_COLOR = 0x21FFFFFF;
+
+ private final Paint mThumbPaint = new Paint();
+ private final Paint mTrackPaint = new Paint();
+ private final RectF mRect = new RectF();
+ private final View mParent;
+
+ public RoundScrollbarRenderer(View parent) {
+ // Paints for the round scrollbar.
+ // Set up the thumb paint
+ mThumbPaint.setAntiAlias(true);
+ mThumbPaint.setStrokeCap(Paint.Cap.ROUND);
+ mThumbPaint.setStyle(Paint.Style.STROKE);
+
+ // Set up the track paint
+ mTrackPaint.setAntiAlias(true);
+ mTrackPaint.setStrokeCap(Paint.Cap.ROUND);
+ mTrackPaint.setStyle(Paint.Style.STROKE);
+
+ mParent = parent;
+ }
+
+ public void drawRoundScrollbars(Canvas canvas, float alpha, Rect bounds) {
+ if (alpha == 0) {
+ return;
+ }
+ // Get information about the current scroll state of the parent view.
+ float maxScroll = mParent.computeVerticalScrollRange();
+ float scrollExtent = mParent.computeVerticalScrollExtent();
+ if (scrollExtent <= 0 || maxScroll <= scrollExtent) {
+ return;
+ }
+ float currentScroll = Math.max(0, mParent.computeVerticalScrollOffset());
+ float linearThumbLength = mParent.computeVerticalScrollExtent();
+ float thumbWidth = mParent.getWidth() * WIDTH_PERCENTAGE;
+ mThumbPaint.setStrokeWidth(thumbWidth);
+ mTrackPaint.setStrokeWidth(thumbWidth);
+
+ setThumbColor(applyAlpha(DEFAULT_THUMB_COLOR, alpha));
+ setTrackColor(applyAlpha(DEFAULT_TRACK_COLOR, alpha));
+
+ // Normalize the sweep angle for the scroll bar.
+ float sweepAngle = (linearThumbLength / maxScroll) * SCROLLBAR_ANGLE_RANGE;
+ sweepAngle = clamp(sweepAngle, MIN_SCROLLBAR_ANGLE_SWIPE, MAX_SCROLLBAR_ANGLE_SWIPE);
+ // Normalize the start angle so that it falls on the track.
+ float startAngle = (currentScroll * (SCROLLBAR_ANGLE_RANGE - sweepAngle))
+ / (maxScroll - linearThumbLength) - SCROLLBAR_ANGLE_RANGE / 2;
+ startAngle = clamp(startAngle, -SCROLLBAR_ANGLE_RANGE / 2,
+ SCROLLBAR_ANGLE_RANGE / 2 - sweepAngle);
+
+ // Draw the track and the scroll bar.
+ mRect.set(
+ bounds.left - thumbWidth / 2,
+ bounds.top,
+ bounds.right - thumbWidth / 2,
+ bounds.bottom);
+
+ canvas.drawArc(mRect, -SCROLLBAR_ANGLE_RANGE / 2, SCROLLBAR_ANGLE_RANGE, false,
+ mTrackPaint);
+ canvas.drawArc(mRect, startAngle, sweepAngle, false, mThumbPaint);
+ }
+
+ private static float clamp(float val, float min, float max) {
+ if (val < min) {
+ return min;
+ } else if (val > max) {
+ return max;
+ } else {
+ return val;
+ }
+ }
+
+ private static int applyAlpha(int color, float alpha) {
+ int alphaByte = (int) (Color.alpha(color) * alpha);
+ return Color.argb(alphaByte, Color.red(color), Color.green(color), Color.blue(color));
+ }
+
+ private void setThumbColor(int thumbColor) {
+ if (mThumbPaint.getColor() != thumbColor) {
+ mThumbPaint.setColor(thumbColor);
+ }
+ }
+
+ private void setTrackColor(int trackColor) {
+ if (mTrackPaint.getColor() != trackColor) {
+ mTrackPaint.setColor(trackColor);
+ }
+ }
+}
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 7da849a..286e097 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -58,6 +58,7 @@
private static native long nativeGetNextFrameNumber(long nativeObject);
private static native int nativeSetScalingMode(long nativeObject, int scalingMode);
+ private static native void nativeSetBuffersTransform(long nativeObject, long transform);
public static final Parcelable.Creator<Surface> CREATOR =
new Parcelable.Creator<Surface>() {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 415e70c..e778a7f 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -51,7 +51,7 @@
private static native void nativeSetLayer(long nativeObject, int zorder);
private static native void nativeSetPosition(long nativeObject, float x, float y);
- private static native void nativeSetPositionAppliesWithResize(long nativeObject);
+ private static native void nativeSetGeometryAppliesWithResize(long nativeObject);
private static native void nativeSetSize(long nativeObject, int w, int h);
private static native void nativeSetTransparentRegionHint(long nativeObject, Region region);
private static native void nativeSetAlpha(long nativeObject, float alpha);
@@ -82,6 +82,10 @@
IBinder displayToken);
private static native int nativeGetActiveConfig(IBinder displayToken);
private static native boolean nativeSetActiveConfig(IBinder displayToken, int id);
+ private static native int[] nativeGetDisplayColorModes(IBinder displayToken);
+ private static native int nativeGetActiveColorMode(IBinder displayToken);
+ private static native boolean nativeSetActiveColorMode(IBinder displayToken,
+ int colorMode);
private static native void nativeSetDisplayPowerMode(
IBinder displayToken, int mode);
private static native void nativeDeferTransactionUntil(long nativeObject,
@@ -89,6 +93,8 @@
private static native void nativeSetOverrideScalingMode(long nativeObject,
int scalingMode);
private static native IBinder nativeGetHandle(long nativeObject);
+ private static native boolean nativeGetTransformToDisplayInverse(long nativeObject);
+
private static native Display.HdrCapabilities nativeGetHdrCapabilities(IBinder displayToken);
@@ -393,6 +399,10 @@
return nativeGetHandle(mNativeObject);
}
+ public boolean getTransformToDisplayInverse() {
+ return nativeGetTransformToDisplayInverse(mNativeObject);
+ }
+
/** flag the transaction as an animation */
public static void setAnimationTransaction() {
nativeSetAnimationTransaction();
@@ -409,13 +419,15 @@
}
/**
- * If the size changes in this transaction, position updates specified
+ * If the buffer size changes in this transaction, position and crop updates specified
* in this transaction will not complete until a buffer of the new size
- * arrives.
+ * arrives. As transform matrix and size are already frozen in this fashion,
+ * this enables totally freezing the surface until the resize has completed
+ * (at which point the geometry influencing aspects of this transaction will then occur)
*/
- public void setPositionAppliesWithResize() {
+ public void setGeometryAppliesWithResize() {
checkNotReleased();
- nativeSetPositionAppliesWithResize(mNativeObject);
+ nativeSetGeometryAppliesWithResize(mNativeObject);
}
public void setSize(int w, int h) {
@@ -539,7 +551,6 @@
public boolean secure;
public long appVsyncOffsetNanos;
public long presentationDeadlineNanos;
- public int colorTransform;
public PhysicalDisplayInfo() {
}
@@ -563,8 +574,7 @@
&& yDpi == other.yDpi
&& secure == other.secure
&& appVsyncOffsetNanos == other.appVsyncOffsetNanos
- && presentationDeadlineNanos == other.presentationDeadlineNanos
- && colorTransform == other.colorTransform;
+ && presentationDeadlineNanos == other.presentationDeadlineNanos;
}
@Override
@@ -582,7 +592,6 @@
secure = other.secure;
appVsyncOffsetNanos = other.appVsyncOffsetNanos;
presentationDeadlineNanos = other.presentationDeadlineNanos;
- colorTransform = other.colorTransform;
}
// For debugging purposes
@@ -591,8 +600,7 @@
return "PhysicalDisplayInfo{" + width + " x " + height + ", " + refreshRate + " fps, "
+ "density " + density + ", " + xDpi + " x " + yDpi + " dpi, secure " + secure
+ ", appVsyncOffset " + appVsyncOffsetNanos
- + ", bufferDeadline " + presentationDeadlineNanos
- + ", colorTransform " + colorTransform + "}";
+ + ", bufferDeadline " + presentationDeadlineNanos + "}";
}
}
@@ -624,6 +632,27 @@
return nativeSetActiveConfig(displayToken, id);
}
+ public static int[] getDisplayColorModes(IBinder displayToken) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ return nativeGetDisplayColorModes(displayToken);
+ }
+
+ public static int getActiveColorMode(IBinder displayToken) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ return nativeGetActiveColorMode(displayToken);
+ }
+
+ public static boolean setActiveColorMode(IBinder displayToken, int colorMode) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ return nativeSetActiveColorMode(displayToken, colorMode);
+ }
+
public static void setDisplayProjection(IBinder displayToken,
int orientation, Rect layerStackRect, Rect displayRect) {
if (displayToken == null) {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 203b825..4818910 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -499,7 +499,7 @@
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
;
- if (!creating && !force && !mUpdateWindowNeeded && !sizeChanged) {
+ if (!creating && !force && !sizeChanged) {
mLayout.privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY;
} else {
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index e650d95..2e0729b 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -16,6 +16,7 @@
package android.view;
+import android.app.ActivityManagerNative;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.content.Context;
@@ -23,6 +24,7 @@
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.drawable.AnimatedVectorDrawable;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -518,16 +520,6 @@
}
/**
- * This method should be invoked whenever the current hardware renderer
- * context should be reset.
- *
- * @param surface The surface to hardware accelerate
- */
- void invalidate(Surface surface) {
- updateSurface(surface);
- }
-
- /**
* Detaches the layer's surface texture from the GL context and releases
* the texture id
*/
@@ -881,6 +873,12 @@
nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode);
}
+ void registerVectorDrawableAnimator(
+ AnimatedVectorDrawable.VectorDrawableAnimatorRT animator) {
+ nRegisterVectorDrawableAnimator(mRootNode.mNativeRenderNode,
+ animator.getAnimatorNativePtr());
+ }
+
public void serializeDisplayListTree() {
nSerializeDisplayListTree(mNativeProxy);
}
@@ -910,10 +908,20 @@
synchronized void init(Context context, long renderProxy) {
if (mInitialized) return;
mInitialized = true;
+ initSched(context, renderProxy);
initGraphicsStats(context, renderProxy);
initAssetAtlas(context, renderProxy);
}
+ private static void initSched(Context context, long renderProxy) {
+ try {
+ int tid = nGetRenderThreadTid(renderProxy);
+ ActivityManagerNative.getDefault().setRenderThread(tid);
+ } catch (Throwable t) {
+ Log.w(LOG_TAG, "Failed to set scheduler for RenderThread", t);
+ }
+ }
+
private static void initGraphicsStats(Context context, long renderProxy) {
try {
IBinder binder = ServiceManager.getService("graphicsstats");
@@ -972,6 +980,7 @@
private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map);
private static native void nSetProcessStatsBuffer(long nativeProxy, int fd);
+ private static native int nGetRenderThreadTid(long nativeProxy);
private static native long nCreateRootRenderNode();
private static native long nCreateProxy(boolean translucent, long rootRenderNode);
@@ -992,6 +1001,7 @@
private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
private static native void nDestroy(long nativeProxy, long rootRenderNode);
private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode);
+ private static native void nRegisterVectorDrawableAnimator(long rootRenderNode, long animator);
private static native void nInvokeFunctor(long functor, boolean waitForCompletion);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 22091c7..c42ad2e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -40,6 +40,7 @@
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Interpolator;
import android.graphics.LinearGradient;
@@ -820,6 +821,16 @@
static boolean sTextureViewIgnoresDrawableSetters = false;
/**
+ * Prior to N, some ViewGroups would not convert LayoutParams properly even though both extend
+ * MarginLayoutParams. For instance, converting LinearLayout.LayoutParams to
+ * RelativeLayout.LayoutParams would lose margin information. This is fixed on N but target API
+ * check is implemented for backwards compatibility.
+ *
+ * {@hide}
+ */
+ protected static boolean sPreserveMarginParamsInLayoutParamConversion;
+
+ /**
* This view does not want keystrokes. Use with TAKES_FOCUS_MASK when
* calling setFlags.
*/
@@ -2435,6 +2446,7 @@
* 1 PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE
* 1 PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED
* 1 PFLAG3_TEMPORARY_DETACH
+ * 1 PFLAG3_NO_REVEAL_ON_FOCUS
* |-------|-------|-------|-------|
*/
@@ -2676,6 +2688,16 @@
*/
static final int PFLAG3_TEMPORARY_DETACH = 0x2000000;
+ /**
+ * Flag indicating that the view does not wish to be revealed within its parent
+ * hierarchy when it gains focus. Expressed in the negative since the historical
+ * default behavior is to reveal on focus; this flag suppresses that behavior.
+ *
+ * @see #setRevealOnFocusHint(boolean)
+ * @see #getRevealOnFocusHint()
+ */
+ private static final int PFLAG3_NO_REVEAL_ON_FOCUS = 0x4000000;
+
/* End of masks for mPrivateFlags3 */
/**
@@ -3748,9 +3770,9 @@
* {@link android.os.Build.VERSION_CODES#N API 24} will be able to participate
* in the drag operation and receive the dragged content.
*
- * If this is the only flag set, then the drag recipient will only have access to text data
+ * <p>If this is the only flag set, then the drag recipient will only have access to text data
* and intents contained in the {@link ClipData} object. Access to URIs contained in the
- * {@link ClipData} is determined by other DRAG_FLAG_GLOBAL_* flags.
+ * {@link ClipData} is determined by other DRAG_FLAG_GLOBAL_* flags</p>
*/
public static final int DRAG_FLAG_GLOBAL = 1 << 8; // 256
@@ -3980,6 +4002,9 @@
*/
String mStartActivityRequestWho;
+ @Nullable
+ private RoundScrollbarRenderer mRoundScrollbarRenderer;
+
/**
* Simple constructor to use when creating a view from code.
*
@@ -4037,6 +4062,10 @@
// On N+, we throw, but that breaks compatibility with apps that use these methods.
sTextureViewIgnoresDrawableSetters = targetSdkVersion <= M;
+ // Prior to N, we would drop margins in LayoutParam conversions. The fix triggers bugs
+ // in apps so we target check it to avoid breaking existing apps.
+ sPreserveMarginParamsInLayoutParamConversion = targetSdkVersion >= N;
+
sCompatibilityDone = true;
}
}
@@ -4215,25 +4244,25 @@
setAlpha(a.getFloat(attr, 1f));
break;
case com.android.internal.R.styleable.View_transformPivotX:
- setPivotX(a.getDimensionPixelOffset(attr, 0));
+ setPivotX(a.getDimension(attr, 0));
break;
case com.android.internal.R.styleable.View_transformPivotY:
- setPivotY(a.getDimensionPixelOffset(attr, 0));
+ setPivotY(a.getDimension(attr, 0));
break;
case com.android.internal.R.styleable.View_translationX:
- tx = a.getDimensionPixelOffset(attr, 0);
+ tx = a.getDimension(attr, 0);
transformSet = true;
break;
case com.android.internal.R.styleable.View_translationY:
- ty = a.getDimensionPixelOffset(attr, 0);
+ ty = a.getDimension(attr, 0);
transformSet = true;
break;
case com.android.internal.R.styleable.View_translationZ:
- tz = a.getDimensionPixelOffset(attr, 0);
+ tz = a.getDimension(attr, 0);
transformSet = true;
break;
case com.android.internal.R.styleable.View_elevation:
- elevation = a.getDimensionPixelOffset(attr, 0);
+ elevation = a.getDimension(attr, 0);
transformSet = true;
break;
case com.android.internal.R.styleable.View_rotation:
@@ -5941,6 +5970,47 @@
}
/**
+ * Sets this view's preference for reveal behavior when it gains focus.
+ *
+ * <p>When set to true, this is a signal to ancestor views in the hierarchy that
+ * this view would prefer to be brought fully into view when it gains focus.
+ * For example, a text field that a user is meant to type into. Other views such
+ * as scrolling containers may prefer to opt-out of this behavior.</p>
+ *
+ * <p>The default value for views is true, though subclasses may change this
+ * based on their preferred behavior.</p>
+ *
+ * @param revealOnFocus true to request reveal on focus in ancestors, false otherwise
+ *
+ * @see #getRevealOnFocusHint()
+ */
+ public final void setRevealOnFocusHint(boolean revealOnFocus) {
+ if (revealOnFocus) {
+ mPrivateFlags3 &= ~PFLAG3_NO_REVEAL_ON_FOCUS;
+ } else {
+ mPrivateFlags3 |= PFLAG3_NO_REVEAL_ON_FOCUS;
+ }
+ }
+
+ /**
+ * Returns this view's preference for reveal behavior when it gains focus.
+ *
+ * <p>When this method returns true for a child view requesting focus, ancestor
+ * views responding to a focus change in {@link ViewParent#requestChildFocus(View, View)}
+ * should make a best effort to make the newly focused child fully visible to the user.
+ * When it returns false, ancestor views should preferably not disrupt scroll positioning or
+ * other properties affecting visibility to the user as part of the focus change.</p>
+ *
+ * @return true if this view would prefer to become fully visible when it gains focus,
+ * false if it would prefer not to disrupt scroll positioning
+ *
+ * @see #setRevealOnFocusHint(boolean)
+ */
+ public final boolean getRevealOnFocusHint() {
+ return (mPrivateFlags3 & PFLAG3_NO_REVEAL_ON_FOCUS) == 0;
+ }
+
+ /**
* Populates <code>outRect</code> with the hotspot bounds. By default,
* the hotspot bounds are identical to the screen bounds.
*
@@ -9781,6 +9851,18 @@
}
/**
+ * Tells whether the {@link View} is in the state between {@link #onStartTemporaryDetach()}
+ * and {@link #onFinishTemporaryDetach()}.
+ *
+ * <p>This method always returns {@code true} when called directly or indirectly from
+ * {@link #onStartTemporaryDetach()}. The return value when called directly or indirectly from
+ * {@link #onFinishTemporaryDetach()}, however, depends on the OS version.
+ * <ul>
+ * <li>{@code true} on {@link android.os.Build.VERSION_CODES#N API 24}</li>
+ * <li>{@code false} on {@link android.os.Build.VERSION_CODES#N_MR1 API 25}} and later</li>
+ * </ul>
+ * </p>
+ *
* @return {@code true} when the View is in the state between {@link #onStartTemporaryDetach()}
* and {@link #onFinishTemporaryDetach()}.
*/
@@ -9815,8 +9897,8 @@
*/
@CallSuper
public void dispatchFinishTemporaryDetach() {
- onFinishTemporaryDetach();
mPrivateFlags3 &= ~PFLAG3_TEMPORARY_DETACH;
+ onFinishTemporaryDetach();
if (hasWindowFocus() && hasFocus()) {
InputMethodManager.getInstance().focusIn(this);
}
@@ -10302,7 +10384,7 @@
* ancestors or by window visibility
* @return true if this view is visible to the user, not counting clipping or overlapping
*/
- @Visibility boolean dispatchVisibilityAggregated(boolean isVisible) {
+ boolean dispatchVisibilityAggregated(boolean isVisible) {
final boolean thisVisible = getVisibility() == VISIBLE;
// If we're not visible but something is telling us we are, ignore it.
if (thisVisible || !isVisible) {
@@ -12306,6 +12388,10 @@
public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) {
ensureTransformationInfo();
if (mTransformationInfo.mAlpha != alpha) {
+ // Report visibility changes, which can affect children, to accessibility
+ if ((alpha == 0) ^ (mTransformationInfo.mAlpha == 0)) {
+ notifySubtreeAccessibilityStateChangedIfNeeded();
+ }
mTransformationInfo.mAlpha = alpha;
if (onSetAlpha((int) (alpha * 255))) {
mPrivateFlags |= PFLAG_ALPHA_SET;
@@ -12316,8 +12402,6 @@
mPrivateFlags &= ~PFLAG_ALPHA_SET;
invalidateViewProperty(true, false);
mRenderNode.setAlpha(getFinalAlpha());
- notifyViewAccessibilityStateChangedIfNeeded(
- AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
}
@@ -14717,6 +14801,25 @@
}
private void getVerticalScrollBarBounds(Rect bounds) {
+ if (mRoundScrollbarRenderer == null) {
+ getStraightVerticalScrollBarBounds(bounds);
+ } else {
+ getRoundVerticalScrollBarBounds(bounds);
+ }
+ }
+
+ private void getRoundVerticalScrollBarBounds(Rect bounds) {
+ final int width = mRight - mLeft;
+ final int height = mBottom - mTop;
+ // Do not take padding into account as we always want the scrollbars
+ // to hug the screen for round wearable devices.
+ bounds.left = mScrollX;
+ bounds.top = mScrollY;
+ bounds.right = bounds.left + width;
+ bounds.bottom = mScrollY + height;
+ }
+
+ private void getStraightVerticalScrollBarBounds(Rect bounds) {
final int inside = (mViewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0;
final int size = getVerticalScrollbarWidth();
int verticalScrollbarPosition = mVerticalScrollbarPosition;
@@ -14751,6 +14854,7 @@
protected final void onDrawScrollBars(Canvas canvas) {
// scrollbars are drawn only when the animation is running
final ScrollabilityCache cache = mScrollCache;
+
if (cache != null) {
int state = cache.state;
@@ -14791,13 +14895,25 @@
final boolean drawVerticalScrollBar = isVerticalScrollBarEnabled()
&& !isVerticalScrollBarHidden();
- if (drawVerticalScrollBar || drawHorizontalScrollBar) {
+ // Fork out the scroll bar drawing for round wearable devices.
+ if (mRoundScrollbarRenderer != null) {
+ if (drawVerticalScrollBar) {
+ final Rect bounds = cache.mScrollBarBounds;
+ getVerticalScrollBarBounds(bounds);
+ mRoundScrollbarRenderer.drawRoundScrollbars(
+ canvas, (float) cache.scrollBar.getAlpha() / 255f, bounds);
+ if (invalidate) {
+ invalidate();
+ }
+ }
+ // Do not draw horizontal scroll bars for round wearable devices.
+ } else if (drawVerticalScrollBar || drawHorizontalScrollBar) {
final ScrollBarDrawable scrollBar = cache.scrollBar;
if (drawHorizontalScrollBar) {
scrollBar.setParameters(computeHorizontalScrollRange(),
- computeHorizontalScrollOffset(),
- computeHorizontalScrollExtent(), false);
+ computeHorizontalScrollOffset(),
+ computeHorizontalScrollExtent(), false);
final Rect bounds = cache.mScrollBarBounds;
getHorizontalScrollBarBounds(bounds);
onDrawHorizontalScrollBar(canvas, scrollBar, bounds.left, bounds.top,
@@ -14809,8 +14925,8 @@
if (drawVerticalScrollBar) {
scrollBar.setParameters(computeVerticalScrollRange(),
- computeVerticalScrollOffset(),
- computeVerticalScrollExtent(), true);
+ computeVerticalScrollOffset(),
+ computeVerticalScrollExtent(), true);
final Rect bounds = cache.mScrollBarBounds;
getVerticalScrollBarBounds(bounds);
onDrawVerticalScrollBar(canvas, scrollBar, bounds.left, bounds.top,
@@ -15411,7 +15527,7 @@
if (vis != GONE) {
onWindowVisibilityChanged(vis);
if (isShown()) {
- // Calling onVisibilityChanged directly here since the subtree will also
+ // Calling onVisibilityAggregated directly here since the subtree will also
// receive dispatchAttachedToWindow and this same call
onVisibilityAggregated(vis == VISIBLE);
}
@@ -17521,6 +17637,15 @@
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
+
+ if (shouldDrawRoundScrollbar()) {
+ if(mRoundScrollbarRenderer == null) {
+ mRoundScrollbarRenderer = new RoundScrollbarRenderer(this);
+ }
+ } else {
+ mRoundScrollbarRenderer = null;
+ }
+
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
ListenerInfo li = mListenerInfo;
@@ -20537,7 +20662,6 @@
* <li>{@link #DRAG_FLAG_GLOBAL}</li>
* <li>{@link #DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION}</li>
* <li>{@link #DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION}</li>
- * <li>{@link #DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION}</li>
* <li>{@link #DRAG_FLAG_GLOBAL_URI_READ}</li>
* <li>{@link #DRAG_FLAG_GLOBAL_URI_WRITE}</li>
* <li>{@link #DRAG_FLAG_OPAQUE}</li>
@@ -22854,7 +22978,7 @@
/**
* Last global system UI visibility reported by the window manager.
*/
- int mGlobalSystemUiVisibility;
+ int mGlobalSystemUiVisibility = -1;
/**
* True if a view in this hierarchy has an OnSystemUiVisibilityChangeListener
@@ -22901,7 +23025,7 @@
final int[] mInvalidateChildLocation = new int[2];
/**
- * Global to the view hierarchy used as a temporary for dealng with
+ * Global to the view hierarchy used as a temporary for dealing with
* computing absolute on-screen location.
*/
final int[] mTmpLocation = new int[2];
@@ -23739,4 +23863,30 @@
stream.addProperty("accessibility:labelFor", getLabelFor());
stream.addProperty("accessibility:importantForAccessibility", getImportantForAccessibility());
}
+
+ /**
+ * Determine if this view is rendered on a round wearable device and is the main view
+ * on the screen.
+ */
+ private boolean shouldDrawRoundScrollbar() {
+ if (!mResources.getConfiguration().isScreenRound()) {
+ return false;
+ }
+
+ final View rootView = getRootView();
+ final WindowInsets insets = getRootWindowInsets();
+
+ int height = getHeight();
+ int width = getWidth();
+ int displayHeight = rootView.getHeight();
+ int displayWidth = rootView.getWidth();
+
+ if (height != displayHeight || width != displayWidth) {
+ return false;
+ }
+
+ getLocationOnScreen(mAttachInfo.mTmpLocation);
+ return mAttachInfo.mTmpLocation[0] == insets.getStableInsetLeft()
+ && mAttachInfo.mTmpLocation[1] == insets.getStableInsetTop();
+ }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 195786d..1bb4c08 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -44,6 +44,7 @@
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
@@ -822,6 +823,13 @@
}
}
+ public void registerVectorDrawableAnimator(
+ AnimatedVectorDrawable.VectorDrawableAnimatorRT animator) {
+ if (mAttachInfo.mHardwareRenderer != null) {
+ mAttachInfo.mHardwareRenderer.registerVectorDrawableAnimator(animator);
+ }
+ }
+
private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
mAttachInfo.mHardwareAccelerated = false;
mAttachInfo.mHardwareAccelerationRequested = false;
@@ -1459,6 +1467,8 @@
final int viewVisibility = getHostVisibility();
final boolean viewVisibilityChanged = !mFirst
&& (mViewVisibility != viewVisibility || mNewSurfaceNeeded);
+ final boolean viewUserVisibilityChanged = !mFirst &&
+ ((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE));
WindowManager.LayoutParams params = null;
if (mWindowAttributesChanged) {
@@ -1532,7 +1542,9 @@
if (viewVisibilityChanged) {
mAttachInfo.mWindowVisibility = viewVisibility;
host.dispatchWindowVisibilityChanged(viewVisibility);
- host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE);
+ if (viewUserVisibilityChanged) {
+ host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE);
+ }
if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
endDragResizing();
destroyHardwareResources();
@@ -1723,7 +1735,7 @@
}
boolean hwInitialized = false;
- boolean framesChanged = false;
+ boolean contentInsetsChanged = false;
boolean hadSurface = mSurface.isValid();
try {
@@ -1763,7 +1775,7 @@
final boolean overscanInsetsChanged = !mPendingOverscanInsets.equals(
mAttachInfo.mOverscanInsets);
- boolean contentInsetsChanged = !mPendingContentInsets.equals(
+ contentInsetsChanged = !mPendingContentInsets.equals(
mAttachInfo.mContentInsets);
final boolean visibleInsetsChanged = !mPendingVisibleInsets.equals(
mAttachInfo.mVisibleInsets);
@@ -1813,19 +1825,6 @@
+ mAttachInfo.mVisibleInsets);
}
- // If any of the insets changed, do a forceLayout on the view so that the
- // measure cache is cleared. We might have a pending MSG_RESIZED_REPORT
- // that is supposed to take care of it, but since pending insets are
- // already modified here, it won't detect the frame change after this.
- framesChanged = overscanInsetsChanged
- || contentInsetsChanged
- || stableInsetsChanged
- || visibleInsetsChanged
- || outsetsChanged;
- if (mAdded && mView != null && framesChanged) {
- forceLayout(mView);
- }
-
if (!hadSurface) {
if (mSurface.isValid()) {
// If we are creating a new surface, then we need to
@@ -2009,7 +2008,7 @@
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
- || mHeight != host.getMeasuredHeight() || framesChanged ||
+ || mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
updatedConfiguration) {
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
@@ -2018,7 +2017,7 @@
+ mWidth + " measuredWidth=" + host.getMeasuredWidth()
+ " mHeight=" + mHeight
+ " measuredHeight=" + host.getMeasuredHeight()
- + " framesChanged=" + framesChanged);
+ + " coveredInsetsChanged=" + contentInsetsChanged);
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
@@ -3178,7 +3177,7 @@
}
focusNode.recycle();
}
- if (mAccessibilityFocusedHost != null) {
+ if ((mAccessibilityFocusedHost != null) && (mAccessibilityFocusedHost != view)) {
// Clear accessibility focus in the view.
mAccessibilityFocusedHost.clearAccessibilityFocusNoCallbacks(
AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
@@ -5507,6 +5506,15 @@
if (mView != null && mAdded) {
final int what = event.mAction;
+ // Cache the drag description when the operation starts, then fill it in
+ // on subsequent calls as a convenience
+ if (what == DragEvent.ACTION_DRAG_STARTED) {
+ mCurrentDragView = null; // Start the current-recipient tracking
+ mDragDescription = event.mClipDescription;
+ } else {
+ event.mClipDescription = mDragDescription;
+ }
+
if (what == DragEvent.ACTION_DRAG_EXITED) {
// A direct EXITED event means that the window manager knows we've just crossed
// a window boundary, so the current drag target within this one must have
@@ -5514,15 +5522,6 @@
// for now.
mView.dispatchDragEvent(event);
} else {
- // Cache the drag description when the operation starts, then fill it in
- // on subsequent calls as a convenience
- if (what == DragEvent.ACTION_DRAG_STARTED) {
- mCurrentDragView = null; // Start the current-recipient tracking
- mDragDescription = event.mClipDescription;
- } else {
- event.mClipDescription = mDragDescription;
- }
-
// For events with a [screen] location, translate into window coordinates
if ((what == DragEvent.ACTION_DRAG_LOCATION) || (what == DragEvent.ACTION_DROP)) {
mDragPoint.set(event.mX, event.mY);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index fe24230..0dbf00d 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -221,6 +221,7 @@
* @see #TYPE_BASE_APPLICATION
* @see #TYPE_APPLICATION
* @see #TYPE_APPLICATION_STARTING
+ * @see #TYPE_DRAWN_APPLICATION
* @see #TYPE_APPLICATION_PANEL
* @see #TYPE_APPLICATION_MEDIA
* @see #TYPE_APPLICATION_SUB_PANEL
@@ -244,6 +245,7 @@
@ViewDebug.IntToString(from = TYPE_BASE_APPLICATION, to = "TYPE_BASE_APPLICATION"),
@ViewDebug.IntToString(from = TYPE_APPLICATION, to = "TYPE_APPLICATION"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_STARTING, to = "TYPE_APPLICATION_STARTING"),
+ @ViewDebug.IntToString(from = TYPE_DRAWN_APPLICATION, to = "TYPE_DRAWN_APPLICATION"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_PANEL, to = "TYPE_APPLICATION_PANEL"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_MEDIA, to = "TYPE_APPLICATION_MEDIA"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_SUB_PANEL, to = "TYPE_APPLICATION_SUB_PANEL"),
@@ -315,6 +317,13 @@
public static final int TYPE_APPLICATION_STARTING = 3;
/**
+ * Window type: a variation on TYPE_APPLICATION that ensures the window
+ * manager will wait for this window to be drawn before the app is shown.
+ * In multiuser systems shows only on the owning user's window.
+ */
+ public static final int TYPE_DRAWN_APPLICATION = 4;
+
+ /**
* End of types of application windows.
*/
public static final int LAST_APPLICATION_WINDOW = 99;
@@ -636,7 +645,7 @@
/**
* Window type: shares similar characteristics with {@link #TYPE_DREAM}. The layer is
- * reserved for screenshot region selection.
+ * reserved for screenshot region selection. These windows must not take input focus.
* @hide
*/
public static final int TYPE_SCREENSHOT = FIRST_SYSTEM_WINDOW + 36;
@@ -1581,6 +1590,15 @@
public static final int ROTATION_ANIMATION_JUMPCUT = 2;
/**
+ * Value for {@link #rotationAnimation} to specify seamless rotation mode.
+ * This works like JUMPCUT but will fall back to CROSSFADE if rotation
+ * can't be applied without pausing the screen.
+ *
+ * @hide
+ */
+ public static final int ROTATION_ANIMATION_SEAMLESS = 3;
+
+ /**
* Define the exit and entry animations used on this window when the device is rotated.
* This only has an affect if the incoming and outgoing topmost
* opaque windows have the #FLAG_FULLSCREEN bit set and are not covered
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index e6f5b83..b52e4b0 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -136,6 +136,12 @@
throws RemoteException;
/**
+ * @return true if windows with FLAG_DISMISS_KEYGUARD should be allowed to show even if
+ * the keyguard is locked.
+ */
+ boolean canShowDismissingWindowWhileLockedLw();
+
+ /**
* Interface to the Window Manager state associated with a particular
* window. You can hold on to an instance of this interface from the call
* to prepareAddWindow() until removeWindow().
@@ -416,6 +422,8 @@
* screen with other application windows.
*/
public boolean isInMultiWindowMode();
+
+ public int getRotationAnimationHint();
}
/**
@@ -476,6 +484,7 @@
public void switchInputMethod(boolean forwardDirection);
public void shutdown(boolean confirm);
+ public void reboot(boolean confirm);
public void rebootSafeMode(boolean confirm);
/**
@@ -1410,4 +1419,6 @@
* Called when the configuration has changed, and it's safe to load new values from resources.
*/
public void onConfigurationChanged();
+
+ public boolean shouldRotateSeamlessly(int oldRotation, int newRotation);
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 81ec330..2dfa8cd 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -305,7 +305,18 @@
return;
}
if (!mIsEnabled) {
- throw new IllegalStateException("Accessibility off. Did you forget to check that?");
+ Looper myLooper = Looper.myLooper();
+ if (myLooper == Looper.getMainLooper()) {
+ throw new IllegalStateException(
+ "Accessibility off. Did you forget to check that?");
+ } else {
+ // If we're not running on the thread with the main looper, it's possible for
+ // the state of accessibility to change between checking isEnabled and
+ // calling this method. So just log the error rather than throwing the
+ // exception.
+ Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled");
+ return;
+ }
}
userId = mUserId;
}
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index a54d94c..351b6db 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -44,6 +44,31 @@
private static final int TOGETHER = 0;
private static final int SEQUENTIALLY = 1;
+ private static class AnimationState {
+ boolean animationClockLocked;
+ long currentVsyncTimeMillis;
+ long lastReportedTimeMillis;
+ };
+
+ private static ThreadLocal<AnimationState> sAnimationState
+ = new ThreadLocal<AnimationState>() {
+ @Override
+ protected AnimationState initialValue() {
+ return new AnimationState();
+ }
+ };
+
+ /** @hide */
+ public static void lockAnimationClock(long vsyncMillis) {
+ AnimationState state = sAnimationState.get();
+ state.animationClockLocked = true;
+ state.currentVsyncTimeMillis = vsyncMillis;
+ }
+
+ /** @hide */
+ public static void unlockAnimationClock() {
+ sAnimationState.get().animationClockLocked = false;
+ }
/**
* Returns the current animation time in milliseconds. This time should be used when invoking
@@ -56,7 +81,14 @@
* @see android.os.SystemClock
*/
public static long currentAnimationTimeMillis() {
- return SystemClock.uptimeMillis();
+ AnimationState state = sAnimationState.get();
+ if (state.animationClockLocked) {
+ // It's important that time never rewinds
+ return Math.max(state.currentVsyncTimeMillis,
+ state.lastReportedTimeMillis);
+ }
+ state.lastReportedTimeMillis = SystemClock.uptimeMillis();
+ return state.lastReportedTimeMillis;
}
/**
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 89dec2d..38962a3 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -851,4 +851,11 @@
endBatchEdit();
}
+
+ /**
+ * The default implementation does nothing.
+ */
+ public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
+ return false;
+ }
}
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 7b7ccae..8038089 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -25,6 +25,8 @@
import android.text.TextUtils;
import android.util.Printer;
+import java.util.Arrays;
+
/**
* An EditorInfo describes several attributes of a text editing object
* that an input method is communicating with (typically an EditText), most
@@ -363,6 +365,18 @@
@Nullable
public LocaleList hintLocales = null;
+
+ /**
+ * List of acceptable MIME types for
+ * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)}.
+ *
+ * <p>{@code null} or an empty array means that
+ * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)} is not supported in this
+ * editor.</p>
+ */
+ @Nullable
+ public String[] contentMimeTypes = null;
+
/**
* Ensure that the data in this EditorInfo is compatible with an application
* that was developed against the given target API version. This can
@@ -418,6 +432,7 @@
+ " fieldName=" + fieldName);
pw.println(prefix + "extras=" + extras);
pw.println(prefix + "hintLocales=" + hintLocales);
+ pw.println(prefix + "contentMimeTypes=" + Arrays.toString(contentMimeTypes));
}
/**
@@ -446,6 +461,7 @@
} else {
LocaleList.getEmptyLocaleList().writeToParcel(dest, flags);
}
+ dest.writeStringArray(contentMimeTypes);
}
/**
@@ -471,6 +487,7 @@
res.extras = source.readBundle();
LocaleList hintLocales = LocaleList.CREATOR.createFromParcel(source);
res.hintLocales = hintLocales.isEmpty() ? null : hintLocales;
+ res.contentMimeTypes = source.readStringArray();
return res;
}
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 9f66429..07910b6 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -16,6 +16,8 @@
package android.view.inputmethod;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Handler;
import android.view.KeyCharacterMap;
@@ -836,4 +838,53 @@
* <p>Note: This does nothing when called from input methods.</p>
*/
public void closeConnection();
+
+ /**
+ * When this flag is used, the editor will be able to request read access to the content URI
+ * contained in the {@link InputContentInfo} object.
+ *
+ * <p>Make sure that the content provider owning the Uri sets the
+ * {@link android.R.styleable#AndroidManifestProvider_grantUriPermissions
+ * grantUriPermissions} attribute in its manifest or included the
+ * {@link android.R.styleable#AndroidManifestGrantUriPermission
+ * <grant-uri-permissions>} tag. Otherwise {@link InputContentInfo#requestPermission()}
+ * can fail.</p>
+ *
+ * <p>Although calling this API is allowed only for the IME that is currently selected, the
+ * client is able to request a temporary read-only access even after the current IME is switched
+ * to any other IME as long as the client keeps {@link InputContentInfo} object.</p>
+ **/
+ public static int INPUT_CONTENT_GRANT_READ_URI_PERMISSION =
+ android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION; // 0x00000001
+
+ /**
+ * Called by the input method to commit a content such as PNG image to the editor.
+ *
+ * <p>In order to avoid variety of compatibility issues, this focuses on a simple use case,
+ * where we expect editors and IMEs work cooperatively as follows:</p>
+ * <ul>
+ * <li>Editor must keep {@link EditorInfo#contentMimeTypes} to be {@code null} if it does
+ * not support this method at all.</li>
+ * <li>Editor can ignore this request when the MIME type specified in
+ * {@code inputContentInfo} does not match to any of {@link EditorInfo#contentMimeTypes}.
+ * </li>
+ * <li>Editor can ignore the cursor position when inserting the provided context.</li>
+ * <li>Editor can return {@code true} asynchronously, even before it starts loading the
+ * content.</li>
+ * <li>Editor should provide a way to delete the content inserted by this method, or revert
+ * the effect caused by this method.</li>
+ * <li>IME should not call this method when there is any composing text, in case calling
+ * this method causes focus change.</li>
+ * <li>IME should grant a permission for the editor to read the content. See
+ * {@link EditorInfo#packageName} about how to obtain the package name of the editor.</li>
+ * </ul>
+ *
+ * @param inputContentInfo Content to be inserted.
+ * @param flags {@code 0} or {@link #INPUT_CONTENT_GRANT_READ_URI_PERMISSION}.
+ * @param opts optional bundle data. This can be {@code null}.
+ * @return {@code true} if this request is accepted by the application, no matter if the request
+ * is already handled or still being handled in background.
+ */
+ public boolean commitContent(@NonNull InputContentInfo inputContentInfo, int flags,
+ @Nullable Bundle opts);
}
diff --git a/core/java/android/view/inputmethod/InputConnectionInspector.java b/core/java/android/view/inputmethod/InputConnectionInspector.java
index 118a61f..2b292bb 100644
--- a/core/java/android/view/inputmethod/InputConnectionInspector.java
+++ b/core/java/android/view/inputmethod/InputConnectionInspector.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Bundle;
import java.lang.annotation.Retention;
import java.lang.reflect.Method;
@@ -41,6 +42,8 @@
MissingMethodFlags.REQUEST_CURSOR_UPDATES,
MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS,
MissingMethodFlags.GET_HANDLER,
+ MissingMethodFlags.CLOSE_CONNECTION,
+ MissingMethodFlags.COMMIT_CONTENT,
})
public @interface MissingMethodFlags {
/**
@@ -78,6 +81,11 @@
* {@link android.os.Build.VERSION_CODES#N} and later.
*/
int CLOSE_CONNECTION = 1 << 6;
+ /**
+ * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)} is available in
+ * {@link android.os.Build.VERSION_CODES#N} MR-1 and later.
+ */
+ int COMMIT_CONTENT = 1 << 7;
}
private static final Map<Class, Integer> sMissingMethodsMap = Collections.synchronizedMap(
@@ -127,6 +135,9 @@
if (!hasCloseConnection(clazz)) {
flags |= MissingMethodFlags.CLOSE_CONNECTION;
}
+ if (!hasCommitContent(clazz)) {
+ flags |= MissingMethodFlags.COMMIT_CONTENT;
+ }
sMissingMethodsMap.put(clazz, flags);
return flags;
}
@@ -195,6 +206,16 @@
}
}
+ private static boolean hasCommitContent(@NonNull final Class clazz) {
+ try {
+ final Method method = clazz.getMethod("commitContent", InputContentInfo.class,
+ int.class, Bundle.class);
+ return !Modifier.isAbstract(method.getModifiers());
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
public static String getMissingMethodFlagsAsString(@MissingMethodFlags final int flags) {
final StringBuilder sb = new StringBuilder();
boolean isEmpty = true;
@@ -242,6 +263,12 @@
}
sb.append("closeConnection()");
}
+ if ((flags & MissingMethodFlags.COMMIT_CONTENT) != 0) {
+ if (!isEmpty) {
+ sb.append(",");
+ }
+ sb.append("commitContent(InputContentInfo, Bundle)");
+ }
return sb.toString();
}
}
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index e743f62..317730c 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -269,4 +269,12 @@
public void closeConnection() {
mTarget.closeConnection();
}
+
+ /**
+ * {@inheritDoc}
+ * @throws NullPointerException if the target is {@code null}.
+ */
+ public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
+ return mTarget.commitContent(inputContentInfo, flags, opts);
+ }
}
diff --git a/telecomm/java/android/telecom/ParcelableCallAnalytics.aidl b/core/java/android/view/inputmethod/InputContentInfo.aidl
similarity index 73%
copy from telecomm/java/android/telecom/ParcelableCallAnalytics.aidl
copy to core/java/android/view/inputmethod/InputContentInfo.aidl
index b7e78d1..1afeee3b 100644
--- a/telecomm/java/android/telecom/ParcelableCallAnalytics.aidl
+++ b/core/java/android/view/inputmethod/InputContentInfo.aidl
@@ -1,11 +1,11 @@
/*
- * Copyright 2016, The Android Open Source Project
+ * 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
+ * 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,
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-package android.telecom;
+package android.view.inputmethod;
-/**
- * {@hide}
- */
-parcelable ParcelableCallAnalytics;
+parcelable InputContentInfo;
diff --git a/core/java/android/view/inputmethod/InputContentInfo.java b/core/java/android/view/inputmethod/InputContentInfo.java
new file mode 100644
index 0000000..b39705e
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputContentInfo.java
@@ -0,0 +1,250 @@
+/*
+ * 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.inputmethod;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ClipDescription;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+import com.android.internal.inputmethod.IInputContentUriToken;
+
+import java.security.InvalidParameterException;
+
+/**
+ * A container object with which input methods can send content files to the target application.
+ */
+public final class InputContentInfo implements Parcelable {
+
+ @NonNull
+ private final Uri mContentUri;
+ @NonNull
+ private final ClipDescription mDescription;
+ @Nullable
+ private final Uri mLinkUri;
+ @NonNull
+ private IInputContentUriToken mUriToken;
+
+ /**
+ * Constructs {@link InputContentInfo} object only with mandatory data.
+ *
+ * @param contentUri Content URI to be exported from the input method.
+ * This cannot be {@code null}.
+ * @param description A {@link ClipDescription} object that contains the metadata of
+ * {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also
+ * {@link ClipDescription#getLabel()} should be describing the content specified by
+ * {@code contentUri} for accessibility reasons.
+ */
+ public InputContentInfo(@NonNull Uri contentUri, @NonNull ClipDescription description) {
+ this(contentUri, description, null /* link Uri */);
+ }
+
+ /**
+ * Constructs {@link InputContentInfo} object with additional link URI.
+ *
+ * @param contentUri Content URI to be exported from the input method.
+ * This cannot be {@code null}.
+ * @param description A {@link ClipDescription} object that contains the metadata of
+ * {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also
+ * {@link ClipDescription#getLabel()} should be describing the content specified by
+ * {@code contentUri} for accessibility reasons.
+ * @param linkUri An optional {@code http} or {@code https} URI. The editor author may provide
+ * a way to navigate the user to the specified web page if this is not {@code null}.
+ * @throws InvalidParameterException if any invalid parameter is specified.
+ */
+ public InputContentInfo(@NonNull Uri contentUri, @NonNull ClipDescription description,
+ @Nullable Uri linkUri) {
+ validateInternal(contentUri, description, linkUri, true /* throwException */);
+ mContentUri = contentUri;
+ mDescription = description;
+ mLinkUri = linkUri;
+ }
+
+ /**
+ * @return {@code true} if all the fields are valid.
+ * @hide
+ */
+ public boolean validate() {
+ return validateInternal(mContentUri, mDescription, mLinkUri, false /* throwException */);
+ }
+
+ /**
+ * Constructs {@link InputContentInfo} object with additional link URI.
+ *
+ * @param contentUri Content URI to be exported from the input method.
+ * This cannot be {@code null}.
+ * @param description A {@link ClipDescription} object that contains the metadata of
+ * {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also
+ * {@link ClipDescription#getLabel()} should be describing the content specified by
+ * {@code contentUri} for accessibility reasons.
+ * @param linkUri An optional {@code http} or {@code https} URI. The editor author may provide
+ * a way to navigate the user to the specified web page if this is not {@code null}.
+ * @param throwException {@code true} if this method should throw an
+ * {@link InvalidParameterException}.
+ * @throws InvalidParameterException if any invalid parameter is specified.
+ */
+ private static boolean validateInternal(@NonNull Uri contentUri,
+ @NonNull ClipDescription description, @Nullable Uri linkUri, boolean throwException) {
+ if (contentUri == null) {
+ if (throwException) {
+ throw new NullPointerException("contentUri");
+ }
+ return false;
+ }
+ if (description == null) {
+ if (throwException) {
+ throw new NullPointerException("description");
+ }
+ return false;
+ }
+ final String contentUriScheme = contentUri.getScheme();
+ if (!"content".equals(contentUriScheme)) {
+ if (throwException) {
+ throw new InvalidParameterException("contentUri must have content scheme");
+ }
+ return false;
+ }
+ if (linkUri != null) {
+ final String scheme = linkUri.getScheme();
+ if (scheme == null ||
+ (!scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https"))) {
+ if (throwException) {
+ throw new InvalidParameterException(
+ "linkUri must have either http or https scheme");
+ }
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @return Content URI with which the content can be obtained.
+ */
+ @NonNull
+ public Uri getContentUri() { return mContentUri; }
+
+ /**
+ * @return {@link ClipDescription} object that contains the metadata of {@code #getContentUri()}
+ * such as MIME type(s). {@link ClipDescription#getLabel()} can be used for accessibility
+ * purpose.
+ */
+ @NonNull
+ public ClipDescription getDescription() { return mDescription; }
+
+ /**
+ * @return An optional {@code http} or {@code https} URI that is related to this content.
+ */
+ @Nullable
+ public Uri getLinkUri() { return mLinkUri; }
+
+ void setUriToken(IInputContentUriToken token) {
+ if (mUriToken != null) {
+ throw new IllegalStateException("URI token is already set");
+ }
+ mUriToken = token;
+ }
+
+ /**
+ * Requests a temporary read-only access permission for content URI associated with this object.
+ *
+ * <p>Does nothing if the temporary permission is already granted.</p>
+ */
+ public void requestPermission() {
+ if (mUriToken == null) {
+ return;
+ }
+ try {
+ mUriToken.take();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Releases a temporary read-only access permission for content URI associated with this object.
+ *
+ * <p>Does nothing if the temporary permission is not granted.</p>
+ */
+ public void releasePermission() {
+ if (mUriToken == null) {
+ return;
+ }
+ try {
+ mUriToken.release();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Used to package this object into a {@link Parcel}.
+ *
+ * @param dest The {@link Parcel} to be written.
+ * @param flags The flags used for parceling.
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ Uri.writeToParcel(dest, mContentUri);
+ mDescription.writeToParcel(dest, flags);
+ Uri.writeToParcel(dest, mLinkUri);
+ if (mUriToken != null) {
+ dest.writeInt(1);
+ dest.writeStrongBinder(mUriToken.asBinder());
+ } else {
+ dest.writeInt(0);
+ }
+ }
+
+ private InputContentInfo(@NonNull Parcel source) {
+ mContentUri = Uri.CREATOR.createFromParcel(source);
+ mDescription = ClipDescription.CREATOR.createFromParcel(source);
+ mLinkUri = Uri.CREATOR.createFromParcel(source);
+ if (source.readInt() == 1) {
+ mUriToken = IInputContentUriToken.Stub.asInterface(source.readStrongBinder());
+ } else {
+ mUriToken = null;
+ }
+ }
+
+ /**
+ * Used to make this class parcelable.
+ */
+ public static final Parcelable.Creator<InputContentInfo> CREATOR
+ = new Parcelable.Creator<InputContentInfo>() {
+ @Override
+ public InputContentInfo createFromParcel(Parcel source) {
+ return new InputContentInfo(source);
+ }
+
+ @Override
+ public InputContentInfo[] newArray(int size) {
+ return new InputContentInfo[size];
+ }
+ };
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 4013b30..c0c8e64 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -16,6 +16,7 @@
package android.view.inputmethod;
+import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.os.SomeArgs;
import com.android.internal.view.IInputConnectionWrapper;
import com.android.internal.view.IInputContext;
@@ -30,6 +31,7 @@
import android.annotation.RequiresPermission;
import android.content.Context;
import android.graphics.Rect;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -56,6 +58,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -2288,6 +2291,40 @@
}
}
+ /**
+ * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
+ * permission to the content.
+ *
+ * <p>See {@link android.inputmethodservice.InputMethodService#exposeContent(InputContentInfo, EditorInfo)}
+ * for details.</p>
+ *
+ * @param token Supplies the identifying token given to an input method when it was started,
+ * which allows it to perform this operation on itself.
+ * @param inputContentInfo Content to be temporarily exposed from the input method to the
+ * application.
+ * This cannot be {@code null}.
+ * @param editorInfo The editor that receives {@link InputContentInfo}.
+ * @hide
+ */
+ public void exposeContent(@NonNull IBinder token, @NonNull InputContentInfo inputContentInfo,
+ @NonNull EditorInfo editorInfo) {
+ final IInputContentUriToken uriToken;
+ final Uri contentUri = inputContentInfo.getContentUri();
+ try {
+ uriToken = mService.createInputContentUriToken(token, contentUri,
+ editorInfo.packageName);
+ if (uriToken == null) {
+ return;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "createInputContentAccessToken failed. contentUri=" + contentUri.toString()
+ + " packageName=" + editorInfo.packageName, e);
+ return;
+ }
+ inputContentInfo.setUriToken(uriToken);
+ return;
+ }
+
void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
final Printer p = new PrintWriterPrinter(fout);
p.println("Input method client state for " + this + ":");
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 5ba1e9d..7b45d8c 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -75,6 +75,7 @@
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.RemoteViews.OnClickHandler;
@@ -1545,7 +1546,7 @@
switch (action) {
case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
case R.id.accessibilityActionScrollDown: {
- if (isEnabled() && getLastVisiblePosition() < getCount() - 1) {
+ if (isEnabled() && canScrollDown()) {
final int viewportHeight = getHeight() - mListPadding.top - mListPadding.bottom;
smoothScrollBy(viewportHeight, PositionScroller.SCROLL_DURATION);
return true;
@@ -1553,7 +1554,7 @@
} return false;
case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
case R.id.accessibilityActionScrollUp: {
- if (isEnabled() && mFirstPosition > 0) {
+ if (isEnabled() && canScrollUp()) {
final int viewportHeight = getHeight() - mListPadding.top - mListPadding.bottom;
smoothScrollBy(-viewportHeight, PositionScroller.SCROLL_DURATION);
return true;
@@ -5976,6 +5977,11 @@
public void closeConnection() {
getTarget().closeConnection();
}
+
+ @Override
+ public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
+ return getTarget().commitContent(inputContentInfo, flags, opts);
+ }
}
/**
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 2fd52b5..af22ec7 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -65,6 +65,8 @@
private SimpleDateFormat mYearFormat;
private SimpleDateFormat mMonthDayFormat;
+ private SimpleDateFormat mAccessibilityEventFormat;
+
// Top-level container.
private ViewGroup mContainer;
@@ -307,6 +309,9 @@
mMonthDayFormat.setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE);
mYearFormat = new SimpleDateFormat("y", locale);
+ // Clear out the lazily-initialized accessibility event formatter.
+ mAccessibilityEventFormat = null;
+
// Update the header text.
onCurrentDateChanged(false);
}
@@ -586,7 +591,12 @@
@Override
public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
- event.getText().add(mCurrentDate.getTime().toString());
+ if (mAccessibilityEventFormat == null) {
+ final String pattern = DateFormat.getBestDateTimePattern(mCurrentLocale, "EMMMMdy");
+ mAccessibilityEventFormat = new SimpleDateFormat(pattern);
+ }
+ final CharSequence text = mAccessibilityEventFormat.format(mCurrentDate.getTime());
+ event.getText().add(text);
}
public CharSequence getAccessibilityClassName() {
diff --git a/core/java/android/widget/ForwardingListener.java b/core/java/android/widget/ForwardingListener.java
index b383e1c..a5fcbc7 100644
--- a/core/java/android/widget/ForwardingListener.java
+++ b/core/java/android/widget/ForwardingListener.java
@@ -58,13 +58,14 @@
public ForwardingListener(View src) {
mSrc = src;
+ src.setLongClickable(true);
+ src.addOnAttachStateChangeListener(this);
+
mScaledTouchSlop = ViewConfiguration.get(src.getContext()).getScaledTouchSlop();
mTapTimeout = ViewConfiguration.getTapTimeout();
// Use a medium-press timeout. Halfway between tap and long-press.
mLongPressTimeout = (mTapTimeout + ViewConfiguration.getLongPressTimeout()) / 2;
-
- src.addOnAttachStateChangeListener(this);
}
/**
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 029313c..b8c74d8 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -382,13 +382,14 @@
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
- if (lp instanceof LayoutParams) {
- return new LayoutParams((LayoutParams) lp);
- } else if (lp instanceof MarginLayoutParams) {
- return new LayoutParams((MarginLayoutParams) lp);
- } else {
- return new LayoutParams(lp);
+ if (sPreserveMarginParamsInLayoutParamConversion) {
+ if (lp instanceof LayoutParams) {
+ return new LayoutParams((LayoutParams) lp);
+ } else if (lp instanceof MarginLayoutParams) {
+ return new LayoutParams((MarginLayoutParams) lp);
+ }
}
+ return new LayoutParams(lp);
}
@Override
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 726586e..af2852c 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -868,13 +868,14 @@
@Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
- if (lp instanceof LayoutParams) {
- return new LayoutParams((LayoutParams) lp);
- } else if (lp instanceof MarginLayoutParams) {
- return new LayoutParams((MarginLayoutParams) lp);
- } else {
- return new LayoutParams(lp);
+ if (sPreserveMarginParamsInLayoutParamConversion) {
+ if (lp instanceof LayoutParams) {
+ return new LayoutParams((LayoutParams) lp);
+ } else if (lp instanceof MarginLayoutParams) {
+ return new LayoutParams((MarginLayoutParams) lp);
+ }
}
+ return new LayoutParams(lp);
}
// Draw grid
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 222a040..aa67c82 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -22,7 +22,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.ColorStateList;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -115,11 +114,17 @@
private int mBaseline = -1;
private boolean mBaselineAlignBottom = false;
- // AdjustViewBounds behavior will be in compatibility mode for older apps.
- private boolean mAdjustViewBoundsCompat = false;
+ /** Compatibility modes dependent on targetSdkVersion of the app. */
+ private static boolean sCompatDone;
+
+ /** AdjustViewBounds behavior will be in compatibility mode for older apps. */
+ private static boolean sCompatAdjustViewBounds;
/** Whether to pass Resources when creating the source from a stream. */
- private boolean mUseCorrectStreamDensity;
+ private static boolean sCompatUseCorrectStreamDensity;
+
+ /** Whether to use pre-Nougat drawable visibility dispatching conditions. */
+ private static boolean sCompatDrawableVisibilityDispatch;
private static final ScaleType[] sScaleTypeArray = {
ScaleType.MATRIX,
@@ -206,9 +211,13 @@
mMatrix = new Matrix();
mScaleType = ScaleType.FIT_CENTER;
- final int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
- mAdjustViewBoundsCompat = targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1;
- mUseCorrectStreamDensity = targetSdkVersion > Build.VERSION_CODES.M;
+ if (!sCompatDone) {
+ final int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
+ sCompatAdjustViewBounds = targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1;
+ sCompatUseCorrectStreamDensity = targetSdkVersion > Build.VERSION_CODES.M;
+ sCompatDrawableVisibilityDispatch = targetSdkVersion < Build.VERSION_CODES.N;
+ sCompatDone = true;
+ }
}
@Override
@@ -545,6 +554,13 @@
* Subsequent calls to {@link #setImageDrawable(Drawable)} will automatically
* mutate the drawable and apply the specified tint and tint mode using
* {@link Drawable#setTintList(ColorStateList)}.
+ * <p>
+ * <em>Note:</em> The default tint mode used by this setter is NOT
+ * consistent with the default tint mode used by the
+ * {@link android.R.styleable#ImageView_tint android:tint}
+ * attribute. If the {@code android:tint} attribute is specified, the
+ * default tint mode will be set to {@link PorterDuff.Mode#SRC_ATOP} to
+ * ensure consistency with earlier versions of the platform.
*
* @param tint the tint to apply, may be {@code null} to clear tint
*
@@ -874,8 +890,8 @@
InputStream stream = null;
try {
stream = mContext.getContentResolver().openInputStream(uri);
- return Drawable.createFromResourceStream(
- mUseCorrectStreamDensity ? getResources() : null, null, stream, null);
+ return Drawable.createFromResourceStream(sCompatUseCorrectStreamDensity
+ ? getResources() : null, null, stream, null);
} catch (Exception e) {
Log.w(LOG_TAG, "Unable to open content: " + uri, e);
} finally {
@@ -910,10 +926,13 @@
mRecycleableBitmapDrawable.setBitmap(null);
}
+ boolean sameDrawable = false;
+
if (mDrawable != null) {
+ sameDrawable = mDrawable == d;
mDrawable.setCallback(null);
unscheduleDrawable(mDrawable);
- if (isAttachedToWindow()) {
+ if (!sCompatDrawableVisibilityDispatch && !sameDrawable && isAttachedToWindow()) {
mDrawable.setVisible(false, false);
}
}
@@ -926,8 +945,11 @@
if (d.isStateful()) {
d.setState(getDrawableState());
}
- if (isAttachedToWindow()) {
- d.setVisible(getWindowVisibility() == VISIBLE && isShown(), true);
+ if (!sameDrawable || sCompatDrawableVisibilityDispatch) {
+ final boolean visible = sCompatDrawableVisibilityDispatch
+ ? getVisibility() == VISIBLE
+ : isAttachedToWindow() && getWindowVisibility() == VISIBLE && isShown();
+ d.setVisible(visible, true);
}
d.setLevel(mLevel);
mDrawableWidth = d.getIntrinsicWidth();
@@ -1051,7 +1073,7 @@
pleft + pright;
// Allow the width to outgrow its original estimate if height is fixed.
- if (!resizeHeight && !mAdjustViewBoundsCompat) {
+ if (!resizeHeight && !sCompatAdjustViewBounds) {
widthSize = resolveAdjustedSize(newWidth, mMaxWidth, widthMeasureSpec);
}
@@ -1067,7 +1089,7 @@
ptop + pbottom;
// Allow the height to outgrow its original estimate if width is fixed.
- if (!resizeWidth && !mAdjustViewBoundsCompat) {
+ if (!resizeWidth && !sCompatAdjustViewBounds) {
heightSize = resolveAdjustedSize(newHeight, mMaxHeight,
heightMeasureSpec);
}
@@ -1506,11 +1528,40 @@
@Override
public void onVisibilityAggregated(boolean isVisible) {
super.onVisibilityAggregated(isVisible);
- if (mDrawable != null) {
+ // Only do this for new apps post-Nougat
+ if (mDrawable != null && !sCompatDrawableVisibilityDispatch) {
mDrawable.setVisible(isVisible, false);
}
}
+ @RemotableViewMethod
+ @Override
+ public void setVisibility(int visibility) {
+ super.setVisibility(visibility);
+ // Only do this for old apps pre-Nougat; new apps use onVisibilityAggregated
+ if (mDrawable != null && sCompatDrawableVisibilityDispatch) {
+ mDrawable.setVisible(visibility == VISIBLE, false);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ // Only do this for old apps pre-Nougat; new apps use onVisibilityAggregated
+ if (mDrawable != null && sCompatDrawableVisibilityDispatch) {
+ mDrawable.setVisible(getVisibility() == VISIBLE, false);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ // Only do this for old apps pre-Nougat; new apps use onVisibilityAggregated
+ if (mDrawable != null && sCompatDrawableVisibilityDispatch) {
+ mDrawable.setVisible(false, false);
+ }
+ }
+
@Override
public CharSequence getAccessibilityClassName() {
return ImageView.class.getName();
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 38d7cd4..f897372 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -1844,13 +1844,14 @@
@Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
- if (lp instanceof LayoutParams) {
- return new LayoutParams((LayoutParams) lp);
- } else if (lp instanceof MarginLayoutParams) {
- return new LayoutParams((MarginLayoutParams) lp);
- } else {
- return new LayoutParams(lp);
+ if (sPreserveMarginParamsInLayoutParamConversion) {
+ if (lp instanceof LayoutParams) {
+ return new LayoutParams((LayoutParams) lp);
+ } else if (lp instanceof MarginLayoutParams) {
+ return new LayoutParams((MarginLayoutParams) lp);
+ }
}
+ return new LayoutParams(lp);
}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index b8b7c55..b0f19d7 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -112,8 +112,8 @@
public boolean isSelectable;
}
- private ArrayList<FixedViewInfo> mHeaderViewInfos = Lists.newArrayList();
- private ArrayList<FixedViewInfo> mFooterViewInfos = Lists.newArrayList();
+ ArrayList<FixedViewInfo> mHeaderViewInfos = Lists.newArrayList();
+ ArrayList<FixedViewInfo> mFooterViewInfos = Lists.newArrayList();
Drawable mDivider;
int mDividerHeight;
@@ -279,7 +279,7 @@
// Wrap the adapter if it wasn't already wrapped.
if (mAdapter != null) {
if (!(mAdapter instanceof HeaderViewListAdapter)) {
- mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
+ wrapHeaderListAdapterInternal();
}
// In the case of re-adding a header view, or adding one later on,
@@ -373,7 +373,7 @@
// Wrap the adapter if it wasn't already wrapped.
if (mAdapter != null) {
if (!(mAdapter instanceof HeaderViewListAdapter)) {
- mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
+ wrapHeaderListAdapterInternal();
}
// In the case of re-adding a footer view, or adding one later on,
@@ -476,7 +476,7 @@
mRecycler.clear();
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
- mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
+ mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;
}
@@ -2050,6 +2050,8 @@
p.recycledHeaderFooter = true;
}
addViewInLayout(child, flowDown ? -1 : 0, p, true);
+ // add view in layout will reset the RTL properties. We have to re-resolve them
+ child.resolveRtlPropertiesIfNeeded();
}
if (needToMeasure) {
@@ -2226,7 +2228,7 @@
* after the header views.
*/
public void setSelectionAfterHeaderView() {
- final int count = mHeaderViewInfos.size();
+ final int count = getHeaderViewsCount();
if (count > 0) {
mNextSelectedPosition = 0;
return;
@@ -3354,7 +3356,7 @@
bounds.right = mRight - mLeft - mPaddingRight;
final int count = getChildCount();
- final int headerCount = mHeaderViewInfos.size();
+ final int headerCount = getHeaderViewsCount();
final int itemCount = mItemCount;
final int footerLimit = (itemCount - mFooterViewInfos.size());
final boolean headerDividers = mHeaderDividersEnabled;
@@ -3938,7 +3940,7 @@
if (drawDividers) {
final boolean fillForMissingDividers = isOpaque() && !super.isOpaque();
final int itemCount = mItemCount;
- final int headerCount = mHeaderViewInfos.size();
+ final int headerCount = getHeaderViewsCount();
final int footerLimit = (itemCount - mFooterViewInfos.size());
final boolean isHeader = (itemIndex < headerCount);
final boolean isFooter = (itemIndex >= footerLimit);
@@ -4050,4 +4052,24 @@
encoder.addProperty("recycleOnMeasure", recycleOnMeasure());
}
+
+ /** @hide */
+ protected HeaderViewListAdapter wrapHeaderListAdapterInternal(
+ ArrayList<ListView.FixedViewInfo> headerViewInfos,
+ ArrayList<ListView.FixedViewInfo> footerViewInfos,
+ ListAdapter adapter) {
+ return new HeaderViewListAdapter(headerViewInfos, footerViewInfos, adapter);
+ }
+
+ /** @hide */
+ protected void wrapHeaderListAdapterInternal() {
+ mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, mAdapter);
+ }
+
+ /** @hide */
+ protected void dispatchDataSetObserverOnChangedInternal() {
+ if (mDataSetObserver != null) {
+ mDataSetObserver.onChanged();
+ }
+ }
}
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index 02ee2df..6f198e7 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -16,7 +16,11 @@
package android.widget;
+import com.android.internal.R;
+import com.android.internal.widget.ExploreByTouchHelper;
+
import android.animation.ObjectAnimator;
+import android.annotation.IntDef;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -43,9 +47,8 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import com.android.internal.R;
-import com.android.internal.widget.ExploreByTouchHelper;
-
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Calendar;
import java.util.Locale;
@@ -55,11 +58,16 @@
* @hide
*/
public class RadialTimePickerView extends View {
-
private static final String TAG = "RadialTimePickerView";
public static final int HOURS = 0;
public static final int MINUTES = 1;
+
+ /** @hide */
+ @IntDef({HOURS, MINUTES})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface PickerType {}
+
private static final int HOURS_INNER = 2;
private static final int SELECTOR_CIRCLE = 0;
@@ -185,8 +193,24 @@
private boolean mInputEnabled = true;
- public interface OnValueSelectedListener {
- void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance);
+ interface OnValueSelectedListener {
+ /**
+ * Called when the selected value at a given picker index has changed.
+ *
+ * @param pickerType the type of value that has changed, one of:
+ * <ul>
+ * <li>{@link #MINUTES}
+ * <li>{@link #HOURS}
+ * </ul>
+ * @param newValue the new value as minute in hour (0-59) or hour in
+ * day (0-23)
+ * @param autoAdvance when the picker type is {@link #HOURS},
+ * {@code true} to switch to the {@link #MINUTES}
+ * picker or {@code false} to stay on the current
+ * picker. No effect when picker type is
+ * {@link #MINUTES}.
+ */
+ void onValueSelected(@PickerType int pickerType, int newValue, boolean autoAdvance);
}
/**
@@ -977,7 +1001,7 @@
// Ensure we're showing the correct picker.
animatePicker(mShowHours, ANIM_DURATION_TOUCH);
- final int type;
+ final @PickerType int type;
final int newValue;
final boolean valueChanged;
diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java
index 2230961..3ad05b5 100644
--- a/core/java/android/widget/RatingBar.java
+++ b/core/java/android/widget/RatingBar.java
@@ -110,8 +110,8 @@
}
// A touch inside a star fill up to that fractional area (slightly more
- // than 1 so boundaries round up).
- mTouchProgressOffset = 1.1f;
+ // than 0.5 so boundaries round up).
+ mTouchProgressOffset = 0.6f;
}
public RatingBar(Context context, AttributeSet attrs) {
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 0136542..a189d3c 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -1104,13 +1104,14 @@
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
- if (lp instanceof LayoutParams) {
- return new LayoutParams((LayoutParams) lp);
- } else if (lp instanceof MarginLayoutParams) {
- return new LayoutParams((MarginLayoutParams) lp);
- } else {
- return new LayoutParams(lp);
+ if (sPreserveMarginParamsInLayoutParamConversion) {
+ if (lp instanceof LayoutParams) {
+ return new LayoutParams((LayoutParams) lp);
+ } else if (lp instanceof MarginLayoutParams) {
+ return new LayoutParams((MarginLayoutParams) lp);
+ }
}
+ return new LayoutParams(lp);
}
/** @hide */
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 1f745185..d0d233e 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -2788,6 +2788,18 @@
}
/**
+ * @hide
+ * Equivalent to calling {@link android.widget.TextView#setTextColor(ColorStateList)}.
+ *
+ * @param viewId The id of the view whose text color should change
+ * @param colors the text colors to set
+ */
+ public void setTextColor(int viewId, @ColorInt ColorStateList colors) {
+ addAction(new ReflectionAction(viewId, "setTextColor", ReflectionAction.COLOR_STATE_LIST,
+ colors));
+ }
+
+ /**
* Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
*
* @param appWidgetId The id of the app widget which contains the specified view. (This
@@ -3195,7 +3207,9 @@
// we don't add a filter to the static version returned by getSystemService.
inflater = inflater.cloneInContext(inflationContext);
inflater.setFilter(this);
- return inflater.inflate(rv.getLayoutId(), parent, false);
+ View v = inflater.inflate(rv.getLayoutId(), parent, false);
+ v.setTagInternal(R.id.widget_frame, rv.getLayoutId());
+ return v;
}
private static void loadTransitionOverride(Context context,
@@ -3373,7 +3387,7 @@
// across orientation change, and has the RemoteViews re-applied in the new orientation,
// we throw an exception, since the layouts may be completely unrelated.
if (hasLandscapeAndPortraitLayouts()) {
- if (v.getId() != rvToApply.getLayoutId()) {
+ if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) {
throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
" that does not share the same root layout id.");
}
@@ -3409,7 +3423,7 @@
// across orientation change, and has the RemoteViews re-applied in the new orientation,
// we throw an exception, since the layouts may be completely unrelated.
if (hasLandscapeAndPortraitLayouts()) {
- if (v.getId() != rvToApply.getLayoutId()) {
+ if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) {
throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
" that does not share the same root layout id.");
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 5cbd284..d3cb742 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3255,6 +3255,7 @@
*
* @attr ref android.R.styleable#TextView_textColor
*/
+ @android.view.RemotableViewMethod
public void setTextColor(ColorStateList colors) {
if (colors == null) {
throw new NullPointerException();
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index c21f1df..aa0b93d 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -61,9 +61,6 @@
private static final int HOUR_INDEX = RadialTimePickerView.HOURS;
private static final int MINUTE_INDEX = RadialTimePickerView.MINUTES;
- // NOT a real index for the purpose of what's showing.
- private static final int AMPM_INDEX = 2;
-
private static final int[] ATTRS_TEXT_COLOR = new int[] {R.attr.textColor};
private static final int[] ATTRS_DISABLED_ALPHA = new int[] {R.attr.disabledAlpha};
@@ -701,22 +698,21 @@
/** Listener for RadialTimePickerView interaction. */
private final OnValueSelectedListener mOnValueSelectedListener = new OnValueSelectedListener() {
@Override
- public void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance) {
- switch (pickerIndex) {
- case HOUR_INDEX:
+ public void onValueSelected(int pickerType, int newValue, boolean autoAdvance) {
+ switch (pickerType) {
+ case RadialTimePickerView.HOURS:
final boolean isTransition = mAllowAutoAdvance && autoAdvance;
setHourInternal(newValue, true, !isTransition);
if (isTransition) {
setCurrentItemShowing(MINUTE_INDEX, true, false);
- mDelegator.announceForAccessibility(newValue + ". " + mSelectMinutes);
+
+ final int localizedHour = getLocalizedHour(newValue);
+ mDelegator.announceForAccessibility(localizedHour + ". " + mSelectMinutes);
}
break;
- case MINUTE_INDEX:
+ case RadialTimePickerView.MINUTES:
setMinuteInternal(newValue, true);
break;
- case AMPM_INDEX:
- updateAmPmLabelStates(newValue);
- break;
}
if (mOnTimeChangedListener != null) {
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 9cdb73a..0988c92 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -161,7 +161,7 @@
private int mTitleMarginTop;
private int mTitleMarginBottom;
- private final RtlSpacingHelper mContentInsets = new RtlSpacingHelper();
+ private RtlSpacingHelper mContentInsets;
private int mContentInsetStartWithNavigation;
private int mContentInsetEndWithActions;
@@ -270,6 +270,7 @@
final int contentInsetRight =
a.getDimensionPixelSize(R.styleable.Toolbar_contentInsetRight, 0);
+ ensureContentInsets();
mContentInsets.setAbsolute(contentInsetLeft, contentInsetRight);
if (contentInsetStart != RtlSpacingHelper.UNDEFINED ||
@@ -463,13 +464,13 @@
*/
public void setTitleMarginBottom(int margin) {
mTitleMarginBottom = margin;
-
requestLayout();
}
@Override
public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
super.onRtlPropertiesChanged(layoutDirection);
+ ensureContentInsets();
mContentInsets.setDirection(layoutDirection == LAYOUT_DIRECTION_RTL);
}
@@ -1092,6 +1093,7 @@
* @attr ref android.R.styleable#Toolbar_contentInsetStart
*/
public void setContentInsetsRelative(int contentInsetStart, int contentInsetEnd) {
+ ensureContentInsets();
mContentInsets.setRelative(contentInsetStart, contentInsetEnd);
}
@@ -1112,7 +1114,7 @@
* @attr ref android.R.styleable#Toolbar_contentInsetStart
*/
public int getContentInsetStart() {
- return mContentInsets.getStart();
+ return mContentInsets != null ? mContentInsets.getStart() : 0;
}
/**
@@ -1132,7 +1134,7 @@
* @attr ref android.R.styleable#Toolbar_contentInsetEnd
*/
public int getContentInsetEnd() {
- return mContentInsets.getEnd();
+ return mContentInsets != null ? mContentInsets.getEnd() : 0;
}
/**
@@ -1154,6 +1156,7 @@
* @attr ref android.R.styleable#Toolbar_contentInsetRight
*/
public void setContentInsetsAbsolute(int contentInsetLeft, int contentInsetRight) {
+ ensureContentInsets();
mContentInsets.setAbsolute(contentInsetLeft, contentInsetRight);
}
@@ -1174,7 +1177,7 @@
* @attr ref android.R.styleable#Toolbar_contentInsetLeft
*/
public int getContentInsetLeft() {
- return mContentInsets.getLeft();
+ return mContentInsets != null ? mContentInsets.getLeft() : 0;
}
/**
@@ -1194,7 +1197,7 @@
* @attr ref android.R.styleable#Toolbar_contentInsetRight
*/
public int getContentInsetRight() {
- return mContentInsets.getRight();
+ return mContentInsets != null ? mContentInsets.getRight() : 0;
}
/**
@@ -2128,6 +2131,12 @@
}
}
+ private void ensureContentInsets() {
+ if (mContentInsets == null) {
+ mContentInsets = new RtlSpacingHelper();
+ }
+ }
+
/**
* Accessor to enable LayoutLib to get ActionMenuPresenter directly.
*/
diff --git a/core/java/com/android/internal/app/AlertActivity.java b/core/java/com/android/internal/app/AlertActivity.java
index ed48b0d..35ffa71 100644
--- a/core/java/com/android/internal/app/AlertActivity.java
+++ b/core/java/com/android/internal/app/AlertActivity.java
@@ -49,7 +49,7 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mAlert = new AlertController(this, this, getWindow());
+ mAlert = AlertController.create(this, this, getWindow());
mAlertParams = new AlertController.AlertParams(this);
}
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index b7ac600..5aeb7f9 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -24,6 +24,7 @@
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
@@ -61,14 +62,15 @@
import java.lang.ref.WeakReference;
public class AlertController {
+ public static final int MICRO = 1;
private final Context mContext;
private final DialogInterface mDialogInterface;
- private final Window mWindow;
+ protected final Window mWindow;
private CharSequence mTitle;
- private CharSequence mMessage;
- private ListView mListView;
+ protected CharSequence mMessage;
+ protected ListView mListView;
private View mView;
private int mViewLayoutResId;
@@ -91,14 +93,14 @@
private CharSequence mButtonNeutralText;
private Message mButtonNeutralMessage;
- private ScrollView mScrollView;
+ protected ScrollView mScrollView;
private int mIconId = 0;
private Drawable mIcon;
private ImageView mIconView;
private TextView mTitleView;
- private TextView mMessageView;
+ protected TextView mMessageView;
private View mCustomTitleView;
private boolean mForceInverseBackground;
@@ -176,7 +178,21 @@
return outValue.data != 0;
}
- public AlertController(Context context, DialogInterface di, Window window) {
+ public static final AlertController create(Context context, DialogInterface di, Window window) {
+ final TypedArray a = context.obtainStyledAttributes(
+ null, R.styleable.AlertDialog, R.attr.alertDialogStyle, 0);
+ int controllerType = a.getInt(R.styleable.AlertDialog_controllerType, 0);
+ a.recycle();
+
+ switch (controllerType) {
+ case MICRO:
+ return new MicroAlertController(context, di, window);
+ default:
+ return new AlertController(context, di, window);
+ }
+ }
+
+ protected AlertController(Context context, DialogInterface di, Window window) {
mContext = context;
mDialogInterface = di;
mWindow = window;
@@ -597,7 +613,7 @@
}
}
- private void setupTitle(ViewGroup topPanel) {
+ protected void setupTitle(ViewGroup topPanel) {
if (mCustomTitleView != null && mShowTitle) {
// Add the custom title view directly to the topPanel layout
final LayoutParams lp = new LayoutParams(
@@ -643,7 +659,7 @@
}
}
- private void setupContent(ViewGroup contentPanel) {
+ protected void setupContent(ViewGroup contentPanel) {
mScrollView = (ScrollView) contentPanel.findViewById(R.id.scrollView);
mScrollView.setFocusable(false);
@@ -680,7 +696,7 @@
}
}
- private void setupButtons(ViewGroup buttonPanel) {
+ protected void setupButtons(ViewGroup buttonPanel) {
int BIT_BUTTON_POSITIVE = 1;
int BIT_BUTTON_NEGATIVE = 2;
int BIT_BUTTON_NEUTRAL = 4;
@@ -1072,7 +1088,8 @@
public void bindView(View view, Context context, Cursor cursor) {
CheckedTextView text = (CheckedTextView) view.findViewById(R.id.text1);
text.setText(cursor.getString(mLabelIndex));
- listView.setItemChecked(cursor.getPosition(),
+ listView.setItemChecked(
+ cursor.getPosition(),
cursor.getInt(mIsCheckedIndex) == 1);
}
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index d552e54..2940079 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -16,10 +16,13 @@
package com.android.internal.app;
+import com.android.internal.R;
+
import android.app.SearchManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
@@ -132,6 +135,16 @@
}
}
+ public void registerVoiceInteractionSessionListener(IVoiceInteractionSessionListener listener) {
+ try {
+ if (mVoiceInteractionManagerService != null) {
+ mVoiceInteractionManagerService.registerVoiceInteractionSessionListener(listener);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to register voice interaction listener", e);
+ }
+ }
+
public ComponentName getAssistComponentForUser(int userId) {
final String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
Settings.Secure.ASSISTANT, userId);
@@ -156,4 +169,41 @@
return null;
}
+ public static boolean isPreinstalledAssistant(Context context, ComponentName assistant) {
+ if (assistant == null) {
+ return false;
+ }
+ ApplicationInfo applicationInfo;
+ try {
+ applicationInfo = context.getPackageManager().getApplicationInfo(
+ assistant.getPackageName(), 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ return applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp();
+ }
+
+ private static boolean isDisclosureEnabled(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.ASSIST_DISCLOSURE_ENABLED, 0) != 0;
+ }
+
+ /**
+ * @return if the disclosure animation should trigger for the given assistant.
+ *
+ * Third-party assistants will always need to disclose, while the user can configure this for
+ * pre-installed assistants.
+ */
+ public static boolean shouldDisclose(Context context, ComponentName assistant) {
+ if (!allowDisablingAssistDisclosure(context)) {
+ return true;
+ }
+
+ return isDisclosureEnabled(context) || !isPreinstalledAssistant(context, assistant);
+ }
+
+ public static boolean allowDisablingAssistDisclosure(Context context) {
+ return context.getResources().getBoolean(
+ com.android.internal.R.bool.config_allowDisablingAssistDisclosure);
+ }
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 216a4f1..5623a2c 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -79,6 +79,8 @@
String newHistoryName, int newType, boolean newUnimportantForLogging);
void noteStopWakelockFromSource(in WorkSource ws, int pid, String name, String historyName,
int type);
+ void noteLongPartialWakelockStart(String name, String historyName, int uid);
+ void noteLongPartialWakelockFinish(String name, String historyName, int uid);
void noteVibratorOn(int uid, long durationMillis);
void noteVibratorOff(int uid);
@@ -118,7 +120,7 @@
void noteWifiBatchedScanStoppedFromSource(in WorkSource ws);
void noteWifiMulticastEnabledFromSource(in WorkSource ws);
void noteWifiMulticastDisabledFromSource(in WorkSource ws);
- void noteWifiRadioPowerState(int powerState, long timestampNs);
+ void noteWifiRadioPowerState(int powerState, long timestampNs, int uid);
void noteNetworkInterfaceType(String iface, int type);
void noteNetworkStatsEnabled();
void noteDeviceIdleMode(int mode, String activeReason, int activeUid);
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 1a963f3..033dd13 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -22,6 +22,7 @@
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.app.IVoiceInteractionSessionListener;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.SoundTrigger;
import android.service.voice.IVoiceInteractionService;
@@ -136,4 +137,9 @@
* Called when the lockscreen got shown.
*/
void onLockscreenShown();
+
+ /**
+ * Register a voice interaction listener.
+ */
+ void registerVoiceInteractionSessionListener(IVoiceInteractionSessionListener listener);
}
diff --git a/core/java/com/android/internal/app/IEphemeralResolver.aidl b/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl
similarity index 63%
copy from core/java/com/android/internal/app/IEphemeralResolver.aidl
copy to core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl
index 40429ee..87749d2 100644
--- a/core/java/com/android/internal/app/IEphemeralResolver.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,11 +14,16 @@
* limitations under the License.
*/
-package com.android.internal.app;
+ package com.android.internal.app;
-import android.content.Intent;
-import android.os.IRemoteCallback;
+ oneway interface IVoiceInteractionSessionListener {
+ /**
+ * Called when a voice session is shown.
+ */
+ void onVoiceSessionShown();
-oneway interface IEphemeralResolver {
- void getEphemeralResolveInfoList(IRemoteCallback callback, int digestPrefix, int sequence);
-}
+ /**
+ * Called when a voice session is hidden.
+ */
+ void onVoiceSessionHidden();
+ }
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/MicroAlertController.java b/core/java/com/android/internal/app/MicroAlertController.java
new file mode 100644
index 0000000..4431f3c
--- /dev/null
+++ b/core/java/com/android/internal/app/MicroAlertController.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import com.android.internal.app.AlertController;
+import com.android.internal.R;
+
+public class MicroAlertController extends AlertController {
+ public MicroAlertController(Context context, DialogInterface di, Window window) {
+ super(context, di, window);
+ }
+
+ @Override
+ protected void setupContent(ViewGroup contentPanel) {
+ // Special case for small screen - the scroll view is higher in hierarchy
+ mScrollView = (ScrollView) mWindow.findViewById(R.id.scrollView);
+
+ // Special case for users that only want to display a String
+ mMessageView = (TextView) contentPanel.findViewById(R.id.message);
+ if (mMessageView == null) {
+ return;
+ }
+
+ if (mMessage != null) {
+ mMessageView.setText(mMessage);
+ } else {
+ // no message, remove associated views
+ mMessageView.setVisibility(View.GONE);
+ contentPanel.removeView(mMessageView);
+
+ if (mListView != null) {
+ // has ListView, swap scrollView with ListView
+
+ // move topPanel into top of scrollParent
+ View topPanel = mScrollView.findViewById(R.id.topPanel);
+ ((ViewGroup) topPanel.getParent()).removeView(topPanel);
+ FrameLayout.LayoutParams topParams =
+ new FrameLayout.LayoutParams(topPanel.getLayoutParams());
+ topParams.gravity = Gravity.TOP;
+ topPanel.setLayoutParams(topParams);
+
+ // move buttonPanel into bottom of scrollParent
+ View buttonPanel = mScrollView.findViewById(R.id.buttonPanel);
+ ((ViewGroup) buttonPanel.getParent()).removeView(buttonPanel);
+ FrameLayout.LayoutParams buttonParams =
+ new FrameLayout.LayoutParams(buttonPanel.getLayoutParams());
+ buttonParams.gravity = Gravity.BOTTOM;
+ buttonPanel.setLayoutParams(buttonParams);
+
+ // remove scrollview
+ final ViewGroup scrollParent = (ViewGroup) mScrollView.getParent();
+ final int childIndex = scrollParent.indexOfChild(mScrollView);
+ scrollParent.removeViewAt(childIndex);
+
+ // add list view
+ scrollParent.addView(mListView,
+ new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+
+ // add top and button panel
+ scrollParent.addView(topPanel);
+ scrollParent.addView(buttonPanel);
+ } else {
+ // no content, just hide everything
+ contentPanel.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ @Override
+ protected void setupTitle(ViewGroup topPanel) {
+ super.setupTitle(topPanel);
+ if (topPanel.getVisibility() == View.GONE) {
+ topPanel.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ @Override
+ protected void setupButtons(ViewGroup buttonPanel) {
+ super.setupButtons(buttonPanel);
+ if (buttonPanel.getVisibility() == View.GONE) {
+ buttonPanel.setVisibility(View.INVISIBLE);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/app/NightDisplayController.java b/core/java/com/android/internal/app/NightDisplayController.java
new file mode 100644
index 0000000..03cd729
--- /dev/null
+++ b/core/java/com/android/internal/app/NightDisplayController.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings.Secure;
+import android.util.Slog;
+
+import com.android.internal.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Calendar;
+import java.util.Locale;
+
+/**
+ * Controller for managing Night display settings.
+ * <p/>
+ * Night display tints your screen red at night. This makes it easier to look at your screen in
+ * dim light and may help you fall asleep more easily.
+ */
+public final class NightDisplayController {
+
+ private static final String TAG = "NightDisplayController";
+ private static final boolean DEBUG = false;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ AUTO_MODE_DISABLED, AUTO_MODE_CUSTOM, AUTO_MODE_TWILIGHT })
+ public @interface AutoMode {}
+
+ /**
+ * Auto mode value to prevent Night display from being automatically activated. It can still
+ * be activated manually via {@link #setActivated(boolean)}.
+ *
+ * @see #setAutoMode(int)
+ */
+ public static final int AUTO_MODE_DISABLED = 0;
+ /**
+ * Auto mode value to automatically activate Night display at a specific start and end time.
+ *
+ * @see #setAutoMode(int)
+ * @see #setCustomStartTime(LocalTime)
+ * @see #setCustomEndTime(LocalTime)
+ */
+ public static final int AUTO_MODE_CUSTOM = 1;
+ /**
+ * Auto mode value to automatically activate Night display from sunset to sunrise.
+ *
+ * @see #setAutoMode(int)
+ */
+ public static final int AUTO_MODE_TWILIGHT = 2;
+
+ private final Context mContext;
+ private final int mUserId;
+
+ private final ContentObserver mContentObserver;
+
+ private Callback mCallback;
+
+ public NightDisplayController(@NonNull Context context) {
+ this(context, UserHandle.myUserId());
+ }
+
+ public NightDisplayController(@NonNull Context context, int userId) {
+ mContext = context.getApplicationContext();
+ mUserId = userId;
+
+ mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+
+ final String setting = uri == null ? null : uri.getLastPathSegment();
+ if (setting != null) {
+ onSettingChanged(setting);
+ }
+ }
+ };
+ }
+
+ /**
+ * Returns {@code true} when Night display is activated (the display is tinted red).
+ */
+ public boolean isActivated() {
+ return Secure.getIntForUser(mContext.getContentResolver(),
+ Secure.NIGHT_DISPLAY_ACTIVATED, 0, mUserId) == 1;
+ }
+
+ /**
+ * Sets whether Night display should be activated.
+ *
+ * @param activated {@code true} if Night display should be activated
+ * @return {@code true} if the activated value was set successfully
+ */
+ public boolean setActivated(boolean activated) {
+ return Secure.putIntForUser(mContext.getContentResolver(),
+ Secure.NIGHT_DISPLAY_ACTIVATED, activated ? 1 : 0, mUserId);
+ }
+
+ /**
+ * Returns the current auto mode value controlling when Night display will be automatically
+ * activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM}, or
+ * {@link #AUTO_MODE_TWILIGHT}.
+ */
+ public @AutoMode int getAutoMode() {
+ int autoMode = Secure.getIntForUser(mContext.getContentResolver(),
+ Secure.NIGHT_DISPLAY_AUTO_MODE, -1, mUserId);
+ if (autoMode == -1) {
+ if (DEBUG) {
+ Slog.d(TAG, "Using default value for setting: " + Secure.NIGHT_DISPLAY_AUTO_MODE);
+ }
+ autoMode = mContext.getResources().getInteger(
+ R.integer.config_defaultNightDisplayAutoMode);
+ }
+
+ if (autoMode != AUTO_MODE_DISABLED
+ && autoMode != AUTO_MODE_CUSTOM
+ && autoMode != AUTO_MODE_TWILIGHT) {
+ Slog.e(TAG, "Invalid autoMode: " + autoMode);
+ autoMode = AUTO_MODE_DISABLED;
+ }
+
+ return autoMode;
+ }
+
+ /**
+ * Sets the current auto mode value controlling when Night display will be automatically
+ * activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM}, or
+ * {@link #AUTO_MODE_TWILIGHT}.
+ *
+ * @param autoMode the new auto mode to use
+ * @return {@code true} if new auto mode was set successfully
+ */
+ public boolean setAutoMode(@AutoMode int autoMode) {
+ if (autoMode != AUTO_MODE_DISABLED
+ && autoMode != AUTO_MODE_CUSTOM
+ && autoMode != AUTO_MODE_TWILIGHT) {
+ throw new IllegalArgumentException("Invalid autoMode: " + autoMode);
+ }
+
+ return Secure.putIntForUser(mContext.getContentResolver(),
+ Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mUserId);
+ }
+
+ /**
+ * Returns the local time when Night display will be automatically activated when using
+ * {@link #AUTO_MODE_CUSTOM}.
+ */
+ public @NonNull LocalTime getCustomStartTime() {
+ int startTimeValue = Secure.getIntForUser(mContext.getContentResolver(),
+ Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, -1, mUserId);
+ if (startTimeValue == -1) {
+ if (DEBUG) {
+ Slog.d(TAG, "Using default value for setting: "
+ + Secure.NIGHT_DISPLAY_CUSTOM_START_TIME);
+ }
+ startTimeValue = mContext.getResources().getInteger(
+ R.integer.config_defaultNightDisplayCustomStartTime);
+ }
+
+ return LocalTime.valueOf(startTimeValue);
+ }
+
+ /**
+ * Sets the local time when Night display will be automatically activated when using
+ * {@link #AUTO_MODE_CUSTOM}.
+ *
+ * @param startTime the local time to automatically activate Night display
+ * @return {@code true} if the new custom start time was set successfully
+ */
+ public boolean setCustomStartTime(@NonNull LocalTime startTime) {
+ if (startTime == null) {
+ throw new IllegalArgumentException("startTime cannot be null");
+ }
+ return Secure.putIntForUser(mContext.getContentResolver(),
+ Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, startTime.toMillis(), mUserId);
+ }
+
+ /**
+ * Returns the local time when Night display will be automatically deactivated when using
+ * {@link #AUTO_MODE_CUSTOM}.
+ */
+ public @NonNull LocalTime getCustomEndTime() {
+ int endTimeValue = Secure.getIntForUser(mContext.getContentResolver(),
+ Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, -1, mUserId);
+ if (endTimeValue == -1) {
+ if (DEBUG) {
+ Slog.d(TAG, "Using default value for setting: "
+ + Secure.NIGHT_DISPLAY_CUSTOM_END_TIME);
+ }
+ endTimeValue = mContext.getResources().getInteger(
+ R.integer.config_defaultNightDisplayCustomEndTime);
+ }
+
+ return LocalTime.valueOf(endTimeValue);
+ }
+
+ /**
+ * Sets the local time when Night display will be automatically deactivated when using
+ * {@link #AUTO_MODE_CUSTOM}.
+ *
+ * @param endTime the local time to automatically deactivate Night display
+ * @return {@code true} if the new custom end time was set successfully
+ */
+ public boolean setCustomEndTime(@NonNull LocalTime endTime) {
+ if (endTime == null) {
+ throw new IllegalArgumentException("endTime cannot be null");
+ }
+ return Secure.putIntForUser(mContext.getContentResolver(),
+ Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.toMillis(), mUserId);
+ }
+
+ private void onSettingChanged(@NonNull String setting) {
+ if (DEBUG) {
+ Slog.d(TAG, "onSettingChanged: " + setting);
+ }
+
+ if (mCallback != null) {
+ switch (setting) {
+ case Secure.NIGHT_DISPLAY_ACTIVATED:
+ mCallback.onActivated(isActivated());
+ break;
+ case Secure.NIGHT_DISPLAY_AUTO_MODE:
+ mCallback.onAutoModeChanged(getAutoMode());
+ break;
+ case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
+ mCallback.onCustomStartTimeChanged(getCustomStartTime());
+ break;
+ case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
+ mCallback.onCustomEndTimeChanged(getCustomEndTime());
+ break;
+ }
+ }
+ }
+
+ /**
+ * Register a callback to be invoked whenever the Night display settings are changed.
+ */
+ public void setListener(Callback callback) {
+ final Callback oldCallback = mCallback;
+ if (oldCallback != callback) {
+ mCallback = callback;
+
+ if (callback == null) {
+ // Stop listening for changes now that there IS NOT a listener.
+ mContext.getContentResolver().unregisterContentObserver(mContentObserver);
+ } else if (oldCallback == null) {
+ // Start listening for changes now that there IS a listener.
+ final ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
+ false /* notifyForDescendants */, mContentObserver, mUserId);
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
+ false /* notifyForDescendants */, mContentObserver, mUserId);
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
+ false /* notifyForDescendants */, mContentObserver, mUserId);
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
+ false /* notifyForDescendants */, mContentObserver, mUserId);
+ }
+ }
+ }
+
+ /**
+ * Returns {@code true} if Night display is supported by the device.
+ */
+ public static boolean isAvailable(Context context) {
+ return context.getResources().getBoolean(R.bool.config_nightDisplayAvailable);
+ }
+
+ /**
+ * A time without a time-zone or date.
+ */
+ public static class LocalTime {
+
+ /**
+ * The hour of the day from 0 - 23.
+ */
+ public final int hourOfDay;
+ /**
+ * The minute within the hour from 0 - 59.
+ */
+ public final int minute;
+
+ public LocalTime(int hourOfDay, int minute) {
+ if (hourOfDay < 0 || hourOfDay > 23) {
+ throw new IllegalArgumentException("Invalid hourOfDay: " + hourOfDay);
+ } else if (minute < 0 || minute > 59) {
+ throw new IllegalArgumentException("Invalid minute: " + minute);
+ }
+
+ this.hourOfDay = hourOfDay;
+ this.minute = minute;
+ }
+
+ /**
+ * Returns the first date time corresponding to this local time that occurs before the
+ * provided date time.
+ *
+ * @param time the date time to compare against
+ * @return the prior date time corresponding to this local time
+ */
+ public Calendar getDateTimeBefore(Calendar time) {
+ final Calendar c = Calendar.getInstance();
+ c.set(Calendar.YEAR, time.get(Calendar.YEAR));
+ c.set(Calendar.DAY_OF_YEAR, time.get(Calendar.DAY_OF_YEAR));
+
+ c.set(Calendar.HOUR_OF_DAY, hourOfDay);
+ c.set(Calendar.MINUTE, minute);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ // Check if the local time has past, if so return the same time tomorrow.
+ if (c.after(time)) {
+ c.add(Calendar.DATE, -1);
+ }
+
+ return c;
+ }
+
+ /**
+ * Returns the first date time corresponding to this local time that occurs after the
+ * provided date time.
+ *
+ * @param time the date time to compare against
+ * @return the next date time corresponding to this local time
+ */
+ public Calendar getDateTimeAfter(Calendar time) {
+ final Calendar c = Calendar.getInstance();
+ c.set(Calendar.YEAR, time.get(Calendar.YEAR));
+ c.set(Calendar.DAY_OF_YEAR, time.get(Calendar.DAY_OF_YEAR));
+
+ c.set(Calendar.HOUR_OF_DAY, hourOfDay);
+ c.set(Calendar.MINUTE, minute);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ // Check if the local time has past, if so return the same time tomorrow.
+ if (c.before(time)) {
+ c.add(Calendar.DATE, 1);
+ }
+
+ return c;
+ }
+
+ /**
+ * Returns a local time corresponding the given number of milliseconds from midnight.
+ *
+ * @param millis the number of milliseconds from midnight
+ * @return the corresponding local time
+ */
+ private static LocalTime valueOf(int millis) {
+ final int hourOfDay = (millis / 3600000) % 24;
+ final int minutes = (millis / 60000) % 60;
+ return new LocalTime(hourOfDay, minutes);
+ }
+
+ /**
+ * Returns the local time represented as milliseconds from midnight.
+ */
+ private int toMillis() {
+ return hourOfDay * 3600000 + minute * 60000;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(Locale.US, "%02d:%02d", hourOfDay, minute);
+ }
+ }
+
+ /**
+ * Callback invoked whenever the Night display settings are changed.
+ */
+ public interface Callback {
+ /**
+ * Callback invoked when the activated state changes.
+ *
+ * @param activated {@code true} if Night display is activated
+ */
+ default void onActivated(boolean activated) {}
+ /**
+ * Callback invoked when the auto mode changes.
+ *
+ * @param autoMode the auto mode to use
+ */
+ default void onAutoModeChanged(int autoMode) {}
+ /**
+ * Callback invoked when the time to automatically activate Night display changes.
+ *
+ * @param startTime the local time to automatically activate Night display
+ */
+ default void onCustomStartTimeChanged(LocalTime startTime) {}
+ /**
+ * Callback invoked when the time to automatically deactivate Night display changes.
+ *
+ * @param endTime the local time to automatically deactivate Night display
+ */
+ default void onCustomEndTimeChanged(LocalTime endTime) {}
+ }
+}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 085e159..0a4ac0d 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -69,9 +69,13 @@
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto;
import com.android.internal.widget.ResolverDrawerLayout;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
@@ -359,6 +363,12 @@
if (isVoiceInteraction()) {
onSetupVoiceInteraction();
}
+ final Set<String> categories = intent.getCategories();
+ MetricsLogger.action(this, mAdapter.hasFilteredItem()
+ ? MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_APP_FEATURED
+ : MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_NONE_FEATURED,
+ intent.getAction() + ":" + intent.getType() + ":"
+ + (categories != null ? Arrays.toString(categories.toArray()) : ""));
}
public final void setFilteredComponents(ComponentName[] components) {
@@ -649,6 +659,19 @@
TargetInfo target = mAdapter.targetInfoForPosition(which, filtered);
if (onTargetSelected(target, always)) {
+ if (always && filtered) {
+ MetricsLogger.action(
+ this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_ALWAYS);
+ } else if (filtered) {
+ MetricsLogger.action(
+ this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_JUST_ONCE);
+ } else {
+ MetricsLogger.action(
+ this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_TAP);
+ }
+ MetricsLogger.action(this, mAdapter.hasFilteredItem()
+ ? MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_APP_FEATURED
+ : MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_NONE_FEATURED);
finish();
}
}
diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
index d24cefe..0a539f1 100644
--- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java
+++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
@@ -37,6 +37,7 @@
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.Window;
import android.widget.TextView;
import com.android.internal.R;
@@ -59,6 +60,9 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ // As this activity has nothing to show, we should hide the title bar also
+ // TODO: Use AlertActivity so we don't need to hide title bar and create a dialog
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
Intent intent = getIntent();
mReason = intent.getIntExtra(EXTRA_UNLAUNCHABLE_REASON, -1);
mUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index 4260e50..951a45a 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -34,7 +34,7 @@
// for AppWidgetHost
//
ParceledListSlice startListening(IAppWidgetHost host, String callingPackage, int hostId,
- in int[] appWidgetIds, out int[] updatedIds);
+ in int[] appWidgetIds);
void stopListening(String callingPackage, int hostId);
int allocateAppWidgetId(String callingPackage, int hostId);
void deleteAppWidgetId(String callingPackage, int appWidgetId);
diff --git a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl b/core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl
similarity index 72%
copy from core/java/com/android/internal/app/EphemeralResolveInfo.aidl
copy to core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl
index 529527b..8abc807 100644
--- a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl
@@ -1,5 +1,5 @@
/*
-** Copyright 2015, The Android Open Source Project
+** 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.
@@ -14,6 +14,14 @@
** limitations under the License.
*/
-package com.android.internal.app;
+package com.android.internal.inputmethod;
-parcelable EphemeralResolveInfo;
+import android.os.IBinder;
+
+/**
+ * {@hide}
+ */
+interface IInputContentUriToken {
+ void take();
+ void release();
+}
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index 5c92f3c..9a5543a 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -176,6 +176,11 @@
* connection.
*/
public boolean isValidLockdownProfile() {
+ // b/7064069: lockdown firewall blocks ports that would be used for PPTP
+ if (type == TYPE_PPTP) {
+ return false;
+ }
+
try {
InetAddress.parseNumericAddress(server);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index a1df8c1..2538d60 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -108,7 +108,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 147 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 150 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -1415,22 +1415,6 @@
mUnpluggedReportedCount = 0;
return true;
}
-
- @Override
- public void writeSummaryFromParcelLocked(Parcel out, long batteryRealtime) {
- super.writeSummaryFromParcelLocked(out, batteryRealtime);
- out.writeLong(mCurrentReportedTotalTime);
- out.writeInt(mCurrentReportedCount);
- out.writeInt(mTrackingReportedValues ? 1 : 0);
- }
-
- @Override
- public void readSummaryFromParcelLocked(Parcel in) {
- super.readSummaryFromParcelLocked(in);
- mUnpluggedReportedTotalTime = mCurrentReportedTotalTime = in.readLong();
- mUnpluggedReportedCount = mCurrentReportedCount = in.readInt();
- mTrackingReportedValues = in.readInt() == 1;
- }
}
/**
@@ -1566,6 +1550,186 @@
}
}
+
+ /**
+ * A StopwatchTimer that also tracks the total and max individual
+ * time spent active according to the given timebase. Whereas
+ * StopwatchTimer apportions the time amongst all in the pool,
+ * the total and max durations are not apportioned.
+ */
+ public static class DurationTimer extends StopwatchTimer {
+ /**
+ * The time (in ms) that the timer was last acquired or the time base
+ * last (re-)started. Increasing the nesting depth does not reset this time.
+ *
+ * -1 if the timer is currently not running or the time base is not running.
+ *
+ * If written to a parcel, the start time is reset, as is mNesting in the base class
+ * StopwatchTimer.
+ */
+ long mStartTimeMs = -1;
+
+ /**
+ * The longest time period (in ms) that the timer has been active.
+ */
+ long mMaxDurationMs;
+
+ /**
+ * The total time (in ms) that that the timer has been active since reset().
+ */
+ long mCurrentDurationMs;
+
+ public DurationTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
+ TimeBase timeBase, Parcel in) {
+ super(clocks, uid, type, timerPool, timeBase, in);
+ mMaxDurationMs = in.readLong();
+ }
+
+ public DurationTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
+ TimeBase timeBase) {
+ super(clocks, uid, type, timerPool, timeBase);
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, long elapsedRealtimeUs) {
+ super.writeToParcel(out, elapsedRealtimeUs);
+ out.writeLong(mMaxDurationMs);
+ }
+
+ /**
+ * Write the summary to the parcel.
+ *
+ * Since the time base is probably meaningless after we come back, reading
+ * from this will have the effect of stopping the timer. So here all we write
+ * is the max duration.
+ */
+ @Override
+ public void writeSummaryFromParcelLocked(Parcel out, long elapsedRealtimeUs) {
+ super.writeSummaryFromParcelLocked(out, elapsedRealtimeUs);
+ out.writeLong(mMaxDurationMs);
+ }
+
+ /**
+ * Read the summary parcel.
+ *
+ * Has the side effect of stopping the timer.
+ */
+ @Override
+ public void readSummaryFromParcelLocked(Parcel in) {
+ super.readSummaryFromParcelLocked(in);
+ mMaxDurationMs = in.readLong();
+ mStartTimeMs = -1;
+ mCurrentDurationMs = 0;
+ }
+
+ /**
+ * The TimeBase time started (again).
+ *
+ * If the timer is also running, store the start time.
+ */
+ public void onTimeStarted(long elapsedRealtimeUs, long baseUptime, long baseRealtime) {
+ super.onTimeStarted(elapsedRealtimeUs, baseUptime, baseRealtime);
+ if (mNesting > 0) {
+ mStartTimeMs = mTimeBase.getRealtime(mClocks.elapsedRealtime()*1000) / 1000;
+ }
+ }
+
+ /**
+ * The TimeBase stopped running.
+ *
+ * If the timer is running, add the duration into mCurrentDurationMs.
+ */
+ @Override
+ public void onTimeStopped(long elapsedRealtimeUs, long baseUptime, long baseRealtime) {
+ super.onTimeStopped(elapsedRealtimeUs, baseUptime, baseRealtime);
+ if (mNesting > 0) {
+ mCurrentDurationMs += (elapsedRealtimeUs / 1000) - mStartTimeMs;
+ }
+ mStartTimeMs = -1;
+ }
+
+ @Override
+ public void logState(Printer pw, String prefix) {
+ super.logState(pw, prefix);
+ }
+
+ @Override
+ public void startRunningLocked(long elapsedRealtimeMs) {
+ super.startRunningLocked(elapsedRealtimeMs);
+ if (mNesting == 1 && mTimeBase.isRunning()) {
+ // Just started
+ mStartTimeMs = mTimeBase.getRealtime(mClocks.elapsedRealtime()*1000) / 1000;
+ }
+ }
+
+ /**
+ * Decrements the mNesting ref-count on this timer.
+ *
+ * If it actually stopped (mNesting went to 0), then possibly update
+ * mMaxDuration if the current duration was the longest ever.
+ */
+ @Override
+ public void stopRunningLocked(long elapsedRealtimeMs) {
+ super.stopRunningLocked(elapsedRealtimeMs);
+ if (mNesting == 0) {
+ final long durationMs = getCurrentDurationMsLocked(elapsedRealtimeMs);
+ if (durationMs > mMaxDurationMs) {
+ mMaxDurationMs = durationMs;
+ }
+ mStartTimeMs = -1;
+ mCurrentDurationMs = 0;
+ }
+ }
+
+ @Override
+ public boolean reset(boolean detachIfReset) {
+ boolean result = super.reset(detachIfReset);
+ mMaxDurationMs = 0;
+ mCurrentDurationMs = 0;
+ if (mNesting > 0) {
+ mStartTimeMs = mTimeBase.getRealtime(mClocks.elapsedRealtime()*1000) / 1000;
+ } else {
+ mStartTimeMs = -1;
+ }
+ return result;
+ }
+
+ /**
+ * Returns the max duration that this timer has ever seen.
+ *
+ * Note that this time is NOT split between the timers in the timer group that
+ * this timer is attached to. It is the TOTAL time.
+ */
+ @Override
+ public long getMaxDurationMsLocked(long elapsedRealtimeMs) {
+ if (mNesting > 0) {
+ final long durationMs = getCurrentDurationMsLocked(elapsedRealtimeMs);
+ if (durationMs > mMaxDurationMs) {
+ return durationMs;
+ }
+ }
+ return mMaxDurationMs;
+ }
+
+ /**
+ * Returns the time since the timer was started.
+ *
+ * Note that this time is NOT split between the timers in the timer group that
+ * this timer is attached to. It is the TOTAL time.
+ */
+ @Override
+ public long getCurrentDurationMsLocked(long elapsedRealtimeMs) {
+ long durationMs = mCurrentDurationMs;
+ if (mNesting > 0) {
+ if (mTimeBase.isRunning()) {
+ durationMs += (mTimeBase.getRealtime(elapsedRealtimeMs*1000)/1000)
+ - mStartTimeMs;
+ }
+ }
+ return durationMs;
+ }
+ }
+
/**
* State for keeping track of timing information.
*/
@@ -1791,11 +1955,17 @@
public abstract class OverflowArrayMap<T> {
private static final String OVERFLOW_NAME = "*overflow*";
+ final int mUid;
final ArrayMap<String, T> mMap = new ArrayMap<>();
T mCurOverflow;
ArrayMap<String, MutableInt> mActiveOverflow;
+ long mLastOverflowTime;
+ long mLastOverflowFinishTime;
+ long mLastClearTime;
+ long mLastCleanupTime;
- public OverflowArrayMap() {
+ public OverflowArrayMap(int uid) {
+ mUid = uid;
}
public ArrayMap<String, T> getMap() {
@@ -1803,6 +1973,7 @@
}
public void clear() {
+ mLastClearTime = SystemClock.elapsedRealtime();
mMap.clear();
mCurOverflow = null;
mActiveOverflow = null;
@@ -1819,6 +1990,7 @@
}
public void cleanup() {
+ mLastCleanupTime = SystemClock.elapsedRealtime();
if (mActiveOverflow != null) {
if (mActiveOverflow.size() == 0) {
mActiveOverflow = null;
@@ -1885,6 +2057,7 @@
mActiveOverflow = new ArrayMap<>();
}
mActiveOverflow.put(name, new MutableInt(1));
+ mLastOverflowTime = SystemClock.elapsedRealtime();
return obj;
}
@@ -1914,6 +2087,7 @@
over.value--;
if (over.value <= 0) {
mActiveOverflow.remove(name);
+ mLastOverflowFinishTime = SystemClock.elapsedRealtime();
}
return obj;
}
@@ -1922,9 +2096,35 @@
// Huh, they are stopping an active operation but we can't find one!
// That's not good.
- Slog.wtf(TAG, "Unable to find object for " + name + " mapsize="
- + mMap.size() + " activeoverflow=" + mActiveOverflow
- + " curoverflow=" + mCurOverflow);
+ StringBuilder sb = new StringBuilder();
+ sb.append("Unable to find object for ");
+ sb.append(name);
+ sb.append(" in uid ");
+ sb.append(mUid);
+ sb.append(" mapsize=");
+ sb.append(mMap.size());
+ sb.append(" activeoverflow=");
+ sb.append(mActiveOverflow);
+ sb.append(" curoverflow=");
+ sb.append(mCurOverflow);
+ long now = SystemClock.elapsedRealtime();
+ if (mLastOverflowTime != 0) {
+ sb.append(" lastOverflowTime=");
+ TimeUtils.formatDuration(mLastOverflowTime-now, sb);
+ }
+ if (mLastOverflowFinishTime != 0) {
+ sb.append(" lastOverflowFinishTime=");
+ TimeUtils.formatDuration(mLastOverflowFinishTime-now, sb);
+ }
+ if (mLastClearTime != 0) {
+ sb.append(" lastClearTime=");
+ TimeUtils.formatDuration(mLastClearTime-now, sb);
+ }
+ if (mLastCleanupTime != 0) {
+ sb.append(" lastCleanupTime=");
+ TimeUtils.formatDuration(mLastCleanupTime-now, sb);
+ }
+ Slog.wtf(TAG, sb.toString());
return null;
}
@@ -3285,6 +3485,36 @@
}
}
+ public void noteLongPartialWakelockStart(String name, String historyName, int uid) {
+ uid = mapUid(uid);
+ final long elapsedRealtime = mClocks.elapsedRealtime();
+ final long uptime = mClocks.uptimeMillis();
+ if (historyName == null) {
+ historyName = name;
+ }
+ if (!mActiveEvents.updateState(HistoryItem.EVENT_LONG_WAKE_LOCK_START, historyName, uid,
+ 0)) {
+ return;
+ }
+ addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_LONG_WAKE_LOCK_START,
+ historyName, uid);
+ }
+
+ public void noteLongPartialWakelockFinish(String name, String historyName, int uid) {
+ uid = mapUid(uid);
+ final long elapsedRealtime = mClocks.elapsedRealtime();
+ final long uptime = mClocks.uptimeMillis();
+ if (historyName == null) {
+ historyName = name;
+ }
+ if (!mActiveEvents.updateState(HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH, historyName, uid,
+ 0)) {
+ return;
+ }
+ addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH,
+ historyName, uid);
+ }
+
void aggregateLastWakeupUptimeLocked(long uptimeMs) {
if (mLastWakeupReason != null) {
long deltaUptime = uptimeMs - mLastWakeupUptimeMs;
@@ -3554,6 +3784,14 @@
mNumConnectivityChange++;
}
+ private void noteMobileRadioApWakeupLocked(final long elapsedRealtimeMillis,
+ final long uptimeMillis, int uid) {
+ uid = mapUid(uid);
+ addHistoryEventLocked(elapsedRealtimeMillis, uptimeMillis, HistoryItem.EVENT_WAKEUP_AP, "",
+ uid);
+ getUidStatsLocked(uid).noteMobileRadioApWakeupLocked();
+ }
+
public void noteMobileRadioPowerState(int powerState, long timestampNs, int uid) {
final long elapsedRealtime = mClocks.elapsedRealtime();
final long uptime = mClocks.uptimeMillis();
@@ -3563,6 +3801,10 @@
powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM
|| powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
if (active) {
+ if (uid > 0) {
+ noteMobileRadioApWakeupLocked(elapsedRealtime, uptime, uid);
+ }
+
mMobileRadioActiveStartTime = realElapsedRealtimeMs = timestampNs / (1000 * 1000);
mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
} else {
@@ -4239,7 +4481,15 @@
}
}
- public void noteWifiRadioPowerState(int powerState, long timestampNs) {
+ private void noteWifiRadioApWakeupLocked(final long elapsedRealtimeMillis,
+ final long uptimeMillis, int uid) {
+ uid = mapUid(uid);
+ addHistoryEventLocked(elapsedRealtimeMillis, uptimeMillis, HistoryItem.EVENT_WAKEUP_AP, "",
+ uid);
+ getUidStatsLocked(uid).noteWifiRadioApWakeupLocked();
+ }
+
+ public void noteWifiRadioPowerState(int powerState, long timestampNs, int uid) {
final long elapsedRealtime = mClocks.elapsedRealtime();
final long uptime = mClocks.uptimeMillis();
if (mWifiRadioPowerState != powerState) {
@@ -4247,6 +4497,9 @@
powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM
|| powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
if (active) {
+ if (uid > 0) {
+ noteWifiRadioApWakeupLocked(elapsedRealtime, uptime, uid);
+ }
mHistoryCur.states |= HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG;
} else {
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG;
@@ -4874,6 +5127,33 @@
return mUidStats;
}
+ private static void detachTimerIfNotNull(BatteryStatsImpl.Timer timer) {
+ if (timer != null) {
+ timer.detach();
+ }
+ }
+
+ private static boolean resetTimerIfNotNull(BatteryStatsImpl.Timer timer,
+ boolean detachIfReset) {
+ if (timer != null) {
+ return timer.reset(detachIfReset);
+ }
+ return true;
+ }
+
+ private static void detachLongCounterIfNotNull(LongSamplingCounter counter) {
+ if (counter != null) {
+ counter.detach();
+ }
+ }
+
+ private static void resetLongCounterIfNotNull(LongSamplingCounter counter,
+ boolean detachIfReset) {
+ if (counter != null) {
+ counter.reset(detachIfReset);
+ }
+ }
+
/**
* The statistics associated with a particular uid.
*/
@@ -4921,6 +5201,16 @@
LongSamplingCounter mMobileRadioActiveCount;
/**
+ * How many times this UID woke up the Application Processor due to a Mobile radio packet.
+ */
+ private LongSamplingCounter mMobileRadioApWakeupCount;
+
+ /**
+ * How many times this UID woke up the Application Processor due to a Wifi packet.
+ */
+ private LongSamplingCounter mWifiRadioApWakeupCount;
+
+ /**
* The amount of time this uid has kept the WiFi controller in idle, tx, and rx mode.
* Can be null if the UID has had no such activity.
*/
@@ -4994,18 +5284,18 @@
mSystemCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
mCpuPower = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
- mWakelockStats = mBsi.new OverflowArrayMap<Wakelock>() {
+ mWakelockStats = mBsi.new OverflowArrayMap<Wakelock>(uid) {
@Override public Wakelock instantiateObject() {
return new Wakelock(mBsi, Uid.this);
}
};
- mSyncStats = mBsi.new OverflowArrayMap<StopwatchTimer>() {
+ mSyncStats = mBsi.new OverflowArrayMap<StopwatchTimer>(uid) {
@Override public StopwatchTimer instantiateObject() {
return new StopwatchTimer(mBsi.mClocks, Uid.this, SYNC, null,
mBsi.mOnBatteryTimeBase);
}
};
- mJobStats = mBsi.new OverflowArrayMap<StopwatchTimer>() {
+ mJobStats = mBsi.new OverflowArrayMap<StopwatchTimer>(uid) {
@Override public StopwatchTimer instantiateObject() {
return new StopwatchTimer(mBsi.mClocks, Uid.this, JOB, null,
mBsi.mOnBatteryTimeBase);
@@ -5629,6 +5919,36 @@
return 0;
}
+ public void noteMobileRadioApWakeupLocked() {
+ if (mMobileRadioApWakeupCount == null) {
+ mMobileRadioApWakeupCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
+ }
+ mMobileRadioApWakeupCount.addCountLocked(1);
+ }
+
+ @Override
+ public long getMobileRadioApWakeupCount(int which) {
+ if (mMobileRadioApWakeupCount != null) {
+ return mMobileRadioApWakeupCount.getCountLocked(which);
+ }
+ return 0;
+ }
+
+ public void noteWifiRadioApWakeupLocked() {
+ if (mWifiRadioApWakeupCount == null) {
+ mWifiRadioApWakeupCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
+ }
+ mWifiRadioApWakeupCount.addCountLocked(1);
+ }
+
+ @Override
+ public long getWifiRadioApWakeupCount(int which) {
+ if (mWifiRadioApWakeupCount != null) {
+ return mWifiRadioApWakeupCount.getCountLocked(which);
+ }
+ return 0;
+ }
+
void initNetworkActivityLocked() {
mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
mNetworkPacketActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
@@ -5671,24 +5991,14 @@
active |= !mWifiMulticastTimer.reset(false);
active |= mWifiMulticastEnabled;
}
- if (mAudioTurnedOnTimer != null) {
- active |= !mAudioTurnedOnTimer.reset(false);
- }
- if (mVideoTurnedOnTimer != null) {
- active |= !mVideoTurnedOnTimer.reset(false);
- }
- if (mFlashlightTurnedOnTimer != null) {
- active |= !mFlashlightTurnedOnTimer.reset(false);
- }
- if (mCameraTurnedOnTimer != null) {
- active |= !mCameraTurnedOnTimer.reset(false);
- }
- if (mForegroundActivityTimer != null) {
- active |= !mForegroundActivityTimer.reset(false);
- }
- if (mBluetoothScanTimer != null) {
- active |= !mBluetoothScanTimer.reset(false);
- }
+
+ active |= !resetTimerIfNotNull(mAudioTurnedOnTimer, false);
+ active |= !resetTimerIfNotNull(mVideoTurnedOnTimer, false);
+ active |= !resetTimerIfNotNull(mFlashlightTurnedOnTimer, false);
+ active |= !resetTimerIfNotNull(mCameraTurnedOnTimer, false);
+ active |= !resetTimerIfNotNull(mForegroundActivityTimer, false);
+ active |= !resetTimerIfNotNull(mBluetoothScanTimer, false);
+
if (mProcessStateTimer != null) {
for (int i = 0; i < NUM_PROCESS_STATE; i++) {
if (mProcessStateTimer[i] != null) {
@@ -5749,6 +6059,9 @@
}
}
+ resetLongCounterIfNotNull(mMobileRadioApWakeupCount, false);
+ resetLongCounterIfNotNull(mWifiRadioApWakeupCount, false);
+
final ArrayMap<String, Wakelock> wakeStats = mWakelockStats.getMap();
for (int iw=wakeStats.size()-1; iw>=0; iw--) {
Wakelock wl = wakeStats.valueAt(iw);
@@ -5908,6 +6221,9 @@
}
}
}
+
+ detachLongCounterIfNotNull(mMobileRadioApWakeupCount);
+ detachLongCounterIfNotNull(mWifiRadioApWakeupCount);
}
return !active;
@@ -6114,6 +6430,20 @@
} else {
out.writeInt(0);
}
+
+ if (mMobileRadioApWakeupCount != null) {
+ out.writeInt(1);
+ mMobileRadioApWakeupCount.writeToParcel(out);
+ } else {
+ out.writeInt(0);
+ }
+
+ if (mWifiRadioApWakeupCount != null) {
+ out.writeInt(1);
+ mWifiRadioApWakeupCount.writeToParcel(out);
+ } else {
+ out.writeInt(0);
+ }
}
void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase, Parcel in) {
@@ -6338,6 +6668,18 @@
} else {
mCpuClusterSpeed = null;
}
+
+ if (in.readInt() != 0) {
+ mMobileRadioApWakeupCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
+ } else {
+ mMobileRadioApWakeupCount = null;
+ }
+
+ if (in.readInt() != 0) {
+ mWifiRadioApWakeupCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
+ } else {
+ mWifiRadioApWakeupCount = null;
+ }
}
/**
@@ -6357,7 +6699,7 @@
/**
* How long (in ms) this uid has been keeping the device partially awake.
*/
- StopwatchTimer mTimerPartial;
+ DurationTimer mTimerPartial;
/**
* How long (in ms) this uid has been keeping the device fully awake.
@@ -6386,8 +6728,8 @@
* @param in the Parcel to be read from.
* return a new Timer, or null.
*/
- private StopwatchTimer readTimerFromParcel(int type, ArrayList<StopwatchTimer> pool,
- TimeBase timeBase, Parcel in) {
+ private StopwatchTimer readStopwatchTimerFromParcel(int type,
+ ArrayList<StopwatchTimer> pool, TimeBase timeBase, Parcel in) {
if (in.readInt() == 0) {
return null;
}
@@ -6395,6 +6737,22 @@
return new StopwatchTimer(mBsi.mClocks, mUid, type, pool, timeBase, in);
}
+ /**
+ * Reads a possibly null Timer from a Parcel. The timer is associated with the
+ * proper timer pool from the given BatteryStatsImpl object.
+ *
+ * @param in the Parcel to be read from.
+ * return a new Timer, or null.
+ */
+ private DurationTimer readDurationTimerFromParcel(int type,
+ ArrayList<StopwatchTimer> pool, TimeBase timeBase, Parcel in) {
+ if (in.readInt() == 0) {
+ return null;
+ }
+
+ return new DurationTimer(mBsi.mClocks, mUid, type, pool, timeBase, in);
+ }
+
boolean reset() {
boolean wlactive = false;
if (mTimerFull != null) {
@@ -6431,11 +6789,14 @@
}
void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase, Parcel in) {
- mTimerPartial = readTimerFromParcel(WAKE_TYPE_PARTIAL,
+ mTimerPartial = readDurationTimerFromParcel(WAKE_TYPE_PARTIAL,
mBsi.mPartialTimers, screenOffTimeBase, in);
- mTimerFull = readTimerFromParcel(WAKE_TYPE_FULL, mBsi.mFullTimers, timeBase, in);
- mTimerWindow = readTimerFromParcel(WAKE_TYPE_WINDOW, mBsi.mWindowTimers, timeBase, in);
- mTimerDraw = readTimerFromParcel(WAKE_TYPE_DRAW, mBsi.mDrawTimers, timeBase, in);
+ mTimerFull = readStopwatchTimerFromParcel(WAKE_TYPE_FULL,
+ mBsi.mFullTimers, timeBase, in);
+ mTimerWindow = readStopwatchTimerFromParcel(WAKE_TYPE_WINDOW,
+ mBsi.mWindowTimers, timeBase, in);
+ mTimerDraw = readStopwatchTimerFromParcel(WAKE_TYPE_DRAW,
+ mBsi.mDrawTimers, timeBase, in);
}
void writeToParcelLocked(Parcel out, long elapsedRealtimeUs) {
@@ -6457,40 +6818,43 @@
}
public StopwatchTimer getStopwatchTimer(int type) {
- StopwatchTimer t;
switch (type) {
- case WAKE_TYPE_PARTIAL:
- t = mTimerPartial;
+ case WAKE_TYPE_PARTIAL: {
+ DurationTimer t = mTimerPartial;
if (t == null) {
- t = new StopwatchTimer(mBsi.mClocks, mUid, WAKE_TYPE_PARTIAL,
+ t = new DurationTimer(mBsi.mClocks, mUid, WAKE_TYPE_PARTIAL,
mBsi.mPartialTimers, mBsi.mOnBatteryScreenOffTimeBase);
mTimerPartial = t;
}
return t;
- case WAKE_TYPE_FULL:
- t = mTimerFull;
+ }
+ case WAKE_TYPE_FULL: {
+ StopwatchTimer t = mTimerFull;
if (t == null) {
t = new StopwatchTimer(mBsi.mClocks, mUid, WAKE_TYPE_FULL,
mBsi.mFullTimers, mBsi.mOnBatteryTimeBase);
mTimerFull = t;
}
return t;
- case WAKE_TYPE_WINDOW:
- t = mTimerWindow;
+ }
+ case WAKE_TYPE_WINDOW: {
+ StopwatchTimer t = mTimerWindow;
if (t == null) {
t = new StopwatchTimer(mBsi.mClocks, mUid, WAKE_TYPE_WINDOW,
mBsi.mWindowTimers, mBsi.mOnBatteryTimeBase);
mTimerWindow = t;
}
return t;
- case WAKE_TYPE_DRAW:
- t = mTimerDraw;
+ }
+ case WAKE_TYPE_DRAW: {
+ StopwatchTimer t = mTimerDraw;
if (t == null) {
t = new StopwatchTimer(mBsi.mClocks, mUid, WAKE_TYPE_DRAW,
mBsi.mDrawTimers, mBsi.mOnBatteryTimeBase);
mTimerDraw = t;
}
return t;
+ }
default:
throw new IllegalArgumentException("type=" + type);
}
@@ -10348,6 +10712,20 @@
u.mCpuClusterSpeed = null;
}
+ if (in.readInt() != 0) {
+ u.mMobileRadioApWakeupCount = new LongSamplingCounter(mOnBatteryTimeBase);
+ u.mMobileRadioApWakeupCount.readSummaryFromParcelLocked(in);
+ } else {
+ u.mMobileRadioApWakeupCount = null;
+ }
+
+ if (in.readInt() != 0) {
+ u.mWifiRadioApWakeupCount = new LongSamplingCounter(mOnBatteryTimeBase);
+ u.mWifiRadioApWakeupCount.readSummaryFromParcelLocked(in);
+ } else {
+ u.mWifiRadioApWakeupCount = null;
+ }
+
int NW = in.readInt();
if (NW > 100) {
throw new ParcelFormatException("File corrupt: too many wake locks " + NW);
@@ -10708,6 +11086,20 @@
out.writeInt(0);
}
+ if (u.mMobileRadioApWakeupCount != null) {
+ out.writeInt(1);
+ u.mMobileRadioApWakeupCount.writeSummaryFromParcelLocked(out);
+ } else {
+ out.writeInt(0);
+ }
+
+ if (u.mWifiRadioApWakeupCount != null) {
+ out.writeInt(1);
+ u.mWifiRadioApWakeupCount.writeSummaryFromParcelLocked(out);
+ } else {
+ out.writeInt(0);
+ }
+
final ArrayMap<String, Uid.Wakelock> wakeStats = u.mWakelockStats.getMap();
int NW = wakeStats.size();
out.writeInt(NW);
diff --git a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
index c828d11..e8919ed 100644
--- a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
+++ b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
@@ -120,7 +120,7 @@
sb.append(" s=");
TimeUtils.formatDuration(systemTimeDeltaUs / 1000, sb);
sb.append(" p=").append(powerDeltaMaUs / 1000).append("mAms");
- Slog.wtf(TAG, sb.toString());
+ Slog.e(TAG, sb.toString());
userTimeDeltaUs = 0;
systemTimeDeltaUs = 0;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 9c960c0..6829961 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -586,21 +586,42 @@
// System server is fully AOTed and never profiled
// for profile guided compilation.
// TODO: Make this configurable between INTERPRET_ONLY, SPEED, SPACE and EVERYTHING?
- final int dexoptNeeded = DexFile.getDexOptNeeded(
+
+ int dexoptNeeded;
+ try {
+ dexoptNeeded = DexFile.getDexOptNeeded(
classPathElement, instructionSet, "speed",
false /* newProfile */);
- if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
- installer.dexopt(classPathElement, Process.SYSTEM_UID, instructionSet,
- dexoptNeeded, 0 /*dexFlags*/, "speed", null /*volumeUuid*/,
- sharedLibraries);
+ } catch (FileNotFoundException ignored) {
+ // Do not add to the classpath.
+ Log.w(TAG, "Missing classpath element for system server: " + classPathElement);
+ continue;
+ } catch (IOException e) {
+ // Not fully clear what to do here as we don't know the cause of the
+ // IO exception. Add to the classpath to be conservative, but don't
+ // attempt to compile it.
+ Log.w(TAG, "Error checking classpath element for system server: "
+ + classPathElement, e);
+ dexoptNeeded = DexFile.NO_DEXOPT_NEEDED;
}
+
+ if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
+ try {
+ installer.dexopt(classPathElement, Process.SYSTEM_UID, instructionSet,
+ dexoptNeeded, 0 /*dexFlags*/, "speed", null /*volumeUuid*/,
+ sharedLibraries);
+ } catch (InstallerException e) {
+ // Ignore (but log), we need this on the classpath for fallback mode.
+ Log.w(TAG, "Failed compiling classpath element for system server: "
+ + classPathElement, e);
+ }
+ }
+
if (!sharedLibraries.isEmpty()) {
sharedLibraries += ":";
}
sharedLibraries += classPathElement;
}
- } catch (IOException | InstallerException e) {
- throw new RuntimeException("Error starting system_server", e);
} finally {
installer.disconnect();
}
@@ -753,7 +774,7 @@
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run();
- } catch (RuntimeException ex) {
+ } catch (Throwable ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index 488f769..619303f 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -371,6 +371,8 @@
systemInsets.bottom);
final int rightInset = DecorView.getColorViewRightInset(stableInsets.right,
systemInsets.right);
+ final int leftInset = DecorView.getColorViewLeftInset(stableInsets.left,
+ systemInsets.left);
if (mStatusBarColor != null) {
mStatusBarColor.setBounds(0, 0, left + width, topInset);
mStatusBarColor.draw(canvas);
@@ -380,9 +382,11 @@
// don't want the navigation bar background be moving around when resizing in docked mode.
// However, we need it for the transitions into/out of docked mode.
if (mNavigationBarColor != null && fullscreen) {
- final int size = DecorView.getNavBarSize(bottomInset, rightInset);
+ final int size = DecorView.getNavBarSize(bottomInset, rightInset, leftInset);
if (DecorView.isNavBarToRightEdge(bottomInset, rightInset)) {
mNavigationBarColor.setBounds(width - size, 0, width, height);
+ } else if (DecorView.isNavBarToLeftEdge(bottomInset, leftInset)) {
+ mNavigationBarColor.setBounds(0, 0, size, height);
} else {
mNavigationBarColor.setBounds(0, height - size, width, height);
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 3cf7a4e..92ab324 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -97,11 +97,11 @@
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
/** @hide */
@@ -162,13 +162,13 @@
private final ColorViewState mStatusColorViewState = new ColorViewState(
SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
- Gravity.TOP, Gravity.LEFT,
+ Gravity.TOP, Gravity.LEFT, Gravity.RIGHT,
Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
com.android.internal.R.id.statusBarBackground,
FLAG_FULLSCREEN);
private final ColorViewState mNavigationColorViewState = new ColorViewState(
SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
- Gravity.BOTTOM, Gravity.RIGHT,
+ Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT,
Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
com.android.internal.R.id.navigationBarBackground,
0 /* hideWindowFlag */);
@@ -184,9 +184,11 @@
private int mLastTopInset = 0;
private int mLastBottomInset = 0;
private int mLastRightInset = 0;
+ private int mLastLeftInset = 0;
private boolean mLastHasTopStableInset = false;
private boolean mLastHasBottomStableInset = false;
private boolean mLastHasRightStableInset = false;
+ private boolean mLastHasLeftStableInset = false;
private int mLastWindowFlags = 0;
private boolean mLastShouldAlwaysConsumeNavBar = false;
@@ -991,12 +993,21 @@
return Math.min(stableRight, systemRight);
}
+ static int getColorViewLeftInset(int stableLeft, int systemLeft) {
+ return Math.min(stableLeft, systemLeft);
+ }
+
static boolean isNavBarToRightEdge(int bottomInset, int rightInset) {
return bottomInset == 0 && rightInset > 0;
}
- static int getNavBarSize(int bottomInset, int rightInset) {
- return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset : bottomInset;
+ static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) {
+ return bottomInset == 0 && leftInset > 0;
+ }
+
+ static int getNavBarSize(int bottomInset, int rightInset, int leftInset) {
+ return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset
+ : isNavBarToLeftEdge(bottomInset, leftInset) ? leftInset : bottomInset;
}
WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
@@ -1016,6 +1027,8 @@
insets.getSystemWindowInsetBottom());
mLastRightInset = getColorViewRightInset(insets.getStableInsetRight(),
insets.getSystemWindowInsetRight());
+ mLastLeftInset = getColorViewRightInset(insets.getStableInsetLeft(),
+ insets.getSystemWindowInsetLeft());
// Don't animate if the presence of stable insets has changed, because that
// indicates that the window was either just added and received them for the
@@ -1031,21 +1044,32 @@
boolean hasRightStableInset = insets.getStableInsetRight() != 0;
disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);
mLastHasRightStableInset = hasRightStableInset;
+
+ boolean hasLeftStableInset = insets.getStableInsetLeft() != 0;
+ disallowAnimate |= (hasLeftStableInset != mLastHasLeftStableInset);
+ mLastHasLeftStableInset = hasLeftStableInset;
+
mLastShouldAlwaysConsumeNavBar = insets.shouldAlwaysConsumeNavBar();
}
boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset);
- int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset);
+ boolean navBarToLeftEdge = isNavBarToLeftEdge(mLastBottomInset, mLastLeftInset);
+ int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset, mLastLeftInset);
updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
- mWindow.mNavigationBarColor, navBarSize, navBarToRightEdge,
- 0 /* rightInset */, animate && !disallowAnimate, false /* force */);
+ mWindow.mNavigationBarColor, navBarSize, navBarToRightEdge || navBarToLeftEdge,
+ navBarToLeftEdge,
+ 0 /* sideInset */, animate && !disallowAnimate, false /* force */);
boolean statusBarNeedsRightInset = navBarToRightEdge
&& mNavigationColorViewState.present;
- int statusBarRightInset = statusBarNeedsRightInset ? mLastRightInset : 0;
+ boolean statusBarNeedsLeftInset = navBarToLeftEdge
+ && mNavigationColorViewState.present;
+ int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset
+ : statusBarNeedsLeftInset ? mLastLeftInset : 0;
updateColorViewInt(mStatusColorViewState, sysUiVisibility,
calculateStatusBarColor(), mLastTopInset,
- false /* matchVertical */, statusBarRightInset, animate && !disallowAnimate,
+ false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset,
+ animate && !disallowAnimate,
mForceWindowDrawsStatusBarBackground);
}
@@ -1070,15 +1094,17 @@
int consumedTop = consumingStatusBar ? mLastTopInset : 0;
int consumedRight = consumingNavBar ? mLastRightInset : 0;
int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
+ int consumedLeft = consumingNavBar ? mLastLeftInset : 0;
if (mContentRoot != null
&& mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams();
if (lp.topMargin != consumedTop || lp.rightMargin != consumedRight
- || lp.bottomMargin != consumedBottom) {
+ || lp.bottomMargin != consumedBottom || lp.leftMargin != consumedLeft) {
lp.topMargin = consumedTop;
lp.rightMargin = consumedRight;
lp.bottomMargin = consumedBottom;
+ lp.leftMargin = consumedLeft;
mContentRoot.setLayoutParams(lp);
if (insets == null) {
@@ -1089,7 +1115,7 @@
}
if (insets != null) {
insets = insets.replaceSystemWindowInsets(
- insets.getSystemWindowInsetLeft(),
+ insets.getSystemWindowInsetLeft() - consumedLeft,
insets.getSystemWindowInsetTop() - consumedTop,
insets.getSystemWindowInsetRight() - consumedRight,
insets.getSystemWindowInsetBottom() - consumedBottom);
@@ -1126,11 +1152,12 @@
* @param size the current size in the non-parent-matching dimension.
* @param verticalBar if true the view is attached to a vertical edge, otherwise to a
* horizontal edge,
- * @param rightMargin rightMargin for the color view.
+ * @param sideMargin sideMargin for the color view.
* @param animate if true, the change will be animated.
*/
private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
- int size, boolean verticalBar, int rightMargin, boolean animate, boolean force) {
+ int size, boolean verticalBar, boolean seascape, int sideMargin,
+ boolean animate, boolean force) {
state.present = (sysUiVis & state.systemUiHideFlag) == 0
&& (mWindow.getAttributes().flags & state.hideWindowFlag) == 0
&& ((mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
@@ -1145,7 +1172,9 @@
int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
- int resolvedGravity = verticalBar ? state.horizontalGravity : state.verticalGravity;
+ int resolvedGravity = verticalBar
+ ? (seascape ? state.seascapeGravity : state.horizontalGravity)
+ : state.verticalGravity;
if (view == null) {
if (showView) {
@@ -1159,7 +1188,11 @@
LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
resolvedGravity);
- lp.rightMargin = rightMargin;
+ if (seascape) {
+ lp.leftMargin = sideMargin;
+ } else {
+ lp.rightMargin = sideMargin;
+ }
addView(view, lp);
updateColorViewTranslations();
}
@@ -1168,12 +1201,16 @@
visibilityChanged = state.targetVisibility != vis;
state.targetVisibility = vis;
LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ int rightMargin = seascape ? 0 : sideMargin;
+ int leftMargin = seascape ? sideMargin : 0;
if (lp.height != resolvedHeight || lp.width != resolvedWidth
- || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) {
+ || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin
+ || lp.leftMargin != leftMargin) {
lp.height = resolvedHeight;
lp.width = resolvedWidth;
lp.gravity = resolvedGravity;
lp.rightMargin = rightMargin;
+ lp.leftMargin = leftMargin;
view.setLayoutParams(lp);
}
if (showView) {
@@ -1824,7 +1861,7 @@
}
final WindowManager.LayoutParams attrs = mWindow.getAttributes();
final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
- attrs.type == TYPE_APPLICATION;
+ attrs.type == TYPE_APPLICATION || attrs.type == TYPE_DRAWN_APPLICATION;
// Only a non floating application window on one of the allowed workspaces can get a caption
if (!mWindow.isFloating() && isApplication && StackId.hasWindowDecor(mStackId)) {
// Dependent on the brightness of the used title we either use the
@@ -2217,17 +2254,19 @@
final int translucentFlag;
final int verticalGravity;
final int horizontalGravity;
+ final int seascapeGravity;
final String transitionName;
final int hideWindowFlag;
ColorViewState(int systemUiHideFlag,
int translucentFlag, int verticalGravity, int horizontalGravity,
- String transitionName, int id, int hideWindowFlag) {
+ int seascapeGravity, String transitionName, int id, int hideWindowFlag) {
this.id = id;
this.systemUiHideFlag = systemUiHideFlag;
this.translucentFlag = translucentFlag;
this.verticalGravity = verticalGravity;
this.horizontalGravity = horizontalGravity;
+ this.seascapeGravity = seascapeGravity;
this.transitionName = transitionName;
this.hideWindowFlag = hideWindowFlag;
}
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index 171a264..83d75fb 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -34,7 +34,7 @@
void addStateMonitorCallback(IKeyguardStateCallback callback);
void verifyUnlock(IKeyguardExitCallback callback);
void keyguardDone(boolean authenticated, boolean wakeup);
- void dismiss();
+ void dismiss(boolean allowWhileOccluded);
void onDreamingStarted();
void onDreamingStopped();
diff --git a/core/java/com/android/internal/policy/IKeyguardStateCallback.aidl b/core/java/com/android/internal/policy/IKeyguardStateCallback.aidl
index db3b40b..8e454db 100644
--- a/core/java/com/android/internal/policy/IKeyguardStateCallback.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardStateCallback.aidl
@@ -19,4 +19,6 @@
void onShowingStateChanged(boolean showing);
void onSimSecureStateChanged(boolean simSecure);
void onInputRestrictedStateChanged(boolean inputRestricted);
+ void onTrustedChanged(boolean trusted);
+ void onHasLockscreenWallpaperChanged(boolean hasLockscreenWallpaper);
}
\ No newline at end of file
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 9ad750d..878f3a6 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -92,8 +92,6 @@
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeProvider;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
@@ -2057,9 +2055,6 @@
}
static private final String FOCUSED_ID_TAG = "android:focusedViewId";
- static private final String ACCESSIBILITY_FOCUSED_ID_TAG = "android:accessibilityFocusedViewId";
- static private final String ACCESSIBILITY_FOCUSED_VIRTUAL_ID_TAG =
- "android:accessibilityFocusedVirtualViewId";
static private final String VIEWS_TAG = "android:views";
static private final String PANELS_TAG = "android:Panels";
static private final String ACTION_BAR_TAG = "android:ActionBar";
@@ -2082,26 +2077,6 @@
outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
}
- // Save the accessibility focused view ID.
- if (mDecor != null) {
- final ViewRootImpl viewRootImpl = mDecor.getViewRootImpl();
- if (viewRootImpl != null) {
- final View accessFocusHost = viewRootImpl.getAccessibilityFocusedHost();
- if (accessFocusHost != null && accessFocusHost.getId() != View.NO_ID) {
- outState.putInt(ACCESSIBILITY_FOCUSED_ID_TAG, accessFocusHost.getId());
-
- // If we have a focused virtual node ID, save that too.
- final AccessibilityNodeInfo accessFocusedNode =
- viewRootImpl.getAccessibilityFocusedVirtualView();
- if (accessFocusedNode != null) {
- final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(
- accessFocusedNode.getSourceNodeId());
- outState.putInt(ACCESSIBILITY_FOCUSED_VIRTUAL_ID_TAG, virtualNodeId);
- }
- }
- }
- }
-
// save the panels
SparseArray<Parcelable> panelStates = new SparseArray<Parcelable>();
savePanelState(panelStates);
@@ -2144,13 +2119,6 @@
}
}
- // Restore the accessibility focused view.
- final int accessFocusHostViewId = savedInstanceState.getInt(
- ACCESSIBILITY_FOCUSED_ID_TAG, View.NO_ID);
- final int accessFocusVirtualViewId = savedInstanceState.getInt(
- ACCESSIBILITY_FOCUSED_VIRTUAL_ID_TAG, AccessibilityNodeInfo.UNDEFINED_ITEM_ID);
- tryRestoreAccessibilityFocus(accessFocusHostViewId, accessFocusVirtualViewId);
-
// Restore the panels.
SparseArray<Parcelable> panelStates = savedInstanceState.getSparseParcelableArray(PANELS_TAG);
if (panelStates != null) {
@@ -2170,33 +2138,6 @@
}
}
- private void tryRestoreAccessibilityFocus(int hostViewId, int virtualViewId) {
- if (hostViewId != View.NO_ID && mDecor != null) {
- final View needsAccessFocus = mDecor.findViewById(hostViewId);
- if (needsAccessFocus != null) {
- if (!tryFocusingVirtualView(needsAccessFocus, virtualViewId)
- && !needsAccessFocus.requestAccessibilityFocus()) {
- Log.w(TAG, "Failed to restore focus to previously accessibility"
- + " focused view with id " + hostViewId);
- }
- } else {
- Log.w(TAG, "Previously accessibility focused view reported id " + hostViewId
- + " during save, but can't be found during restore.");
- }
- }
- }
-
- private boolean tryFocusingVirtualView(View host, int virtualViewId) {
- if (virtualViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
- final AccessibilityNodeProvider nodeProvider = host.getAccessibilityNodeProvider();
- if (nodeProvider != null) {
- return nodeProvider.performAction(virtualViewId,
- AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
- }
- }
- return false;
- }
-
/**
* Invoked when the panels should freeze their state.
*
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 7706ff7..20f10b3 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -112,4 +112,5 @@
void addQsTile(in ComponentName tile);
void remQsTile(in ComponentName tile);
void clickQsTile(in ComponentName tile);
+ void handleSystemNavigationKey(in int key);
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 3d05422..698e387 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -66,4 +66,5 @@
void addTile(in ComponentName tile);
void remTile(in ComponentName tile);
void clickTile(in ComponentName tile);
+ void handleSystemNavigationKey(in int key);
}
diff --git a/core/java/com/android/internal/util/MimeIconUtils.java b/core/java/com/android/internal/util/MimeIconUtils.java
new file mode 100644
index 0000000..841ec7c
--- /dev/null
+++ b/core/java/com/android/internal/util/MimeIconUtils.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.provider.DocumentsContract;
+
+import com.android.internal.R;
+
+import java.util.HashMap;
+
+public class MimeIconUtils {
+
+ private static HashMap<String, Integer> sMimeIcons = new HashMap<>();
+
+ private static void add(String mimeType, int resId) {
+ if (sMimeIcons.put(mimeType, resId) != null) {
+ throw new RuntimeException(mimeType + " already registered!");
+ }
+ }
+
+ static {
+ int icon;
+
+ // Package
+ icon = R.drawable.ic_doc_apk;
+ add("application/vnd.android.package-archive", icon);
+
+ // Audio
+ icon = R.drawable.ic_doc_audio;
+ add("application/ogg", icon);
+ add("application/x-flac", icon);
+
+ // Certificate
+ icon = R.drawable.ic_doc_certificate;
+ add("application/pgp-keys", icon);
+ add("application/pgp-signature", icon);
+ add("application/x-pkcs12", icon);
+ add("application/x-pkcs7-certreqresp", icon);
+ add("application/x-pkcs7-crl", icon);
+ add("application/x-x509-ca-cert", icon);
+ add("application/x-x509-user-cert", icon);
+ add("application/x-pkcs7-certificates", icon);
+ add("application/x-pkcs7-mime", icon);
+ add("application/x-pkcs7-signature", icon);
+
+ // Source code
+ icon = R.drawable.ic_doc_codes;
+ add("application/rdf+xml", icon);
+ add("application/rss+xml", icon);
+ add("application/x-object", icon);
+ add("application/xhtml+xml", icon);
+ add("text/css", icon);
+ add("text/html", icon);
+ add("text/xml", icon);
+ add("text/x-c++hdr", icon);
+ add("text/x-c++src", icon);
+ add("text/x-chdr", icon);
+ add("text/x-csrc", icon);
+ add("text/x-dsrc", icon);
+ add("text/x-csh", icon);
+ add("text/x-haskell", icon);
+ add("text/x-java", icon);
+ add("text/x-literate-haskell", icon);
+ add("text/x-pascal", icon);
+ add("text/x-tcl", icon);
+ add("text/x-tex", icon);
+ add("application/x-latex", icon);
+ add("application/x-texinfo", icon);
+ add("application/atom+xml", icon);
+ add("application/ecmascript", icon);
+ add("application/json", icon);
+ add("application/javascript", icon);
+ add("application/xml", icon);
+ add("text/javascript", icon);
+ add("application/x-javascript", icon);
+
+ // Compressed
+ icon = R.drawable.ic_doc_compressed;
+ add("application/mac-binhex40", icon);
+ add("application/rar", icon);
+ add("application/zip", icon);
+ add("application/x-apple-diskimage", icon);
+ add("application/x-debian-package", icon);
+ add("application/x-gtar", icon);
+ add("application/x-iso9660-image", icon);
+ add("application/x-lha", icon);
+ add("application/x-lzh", icon);
+ add("application/x-lzx", icon);
+ add("application/x-stuffit", icon);
+ add("application/x-tar", icon);
+ add("application/x-webarchive", icon);
+ add("application/x-webarchive-xml", icon);
+ add("application/gzip", icon);
+ add("application/x-7z-compressed", icon);
+ add("application/x-deb", icon);
+ add("application/x-rar-compressed", icon);
+
+ // Contact
+ icon = R.drawable.ic_doc_contact;
+ add("text/x-vcard", icon);
+ add("text/vcard", icon);
+
+ // Event
+ icon = R.drawable.ic_doc_event;
+ add("text/calendar", icon);
+ add("text/x-vcalendar", icon);
+
+ // Font
+ icon = R.drawable.ic_doc_font;
+ add("application/x-font", icon);
+ add("application/font-woff", icon);
+ add("application/x-font-woff", icon);
+ add("application/x-font-ttf", icon);
+
+ // Image
+ icon = R.drawable.ic_doc_image;
+ add("application/vnd.oasis.opendocument.graphics", icon);
+ add("application/vnd.oasis.opendocument.graphics-template", icon);
+ add("application/vnd.oasis.opendocument.image", icon);
+ add("application/vnd.stardivision.draw", icon);
+ add("application/vnd.sun.xml.draw", icon);
+ add("application/vnd.sun.xml.draw.template", icon);
+
+ // PDF
+ icon = R.drawable.ic_doc_pdf;
+ add("application/pdf", icon);
+
+ // Presentation
+ icon = R.drawable.ic_doc_presentation;
+ add("application/vnd.stardivision.impress", icon);
+ add("application/vnd.sun.xml.impress", icon);
+ add("application/vnd.sun.xml.impress.template", icon);
+ add("application/x-kpresenter", icon);
+ add("application/vnd.oasis.opendocument.presentation", icon);
+
+ // Spreadsheet
+ icon = R.drawable.ic_doc_spreadsheet;
+ add("application/vnd.oasis.opendocument.spreadsheet", icon);
+ add("application/vnd.oasis.opendocument.spreadsheet-template", icon);
+ add("application/vnd.stardivision.calc", icon);
+ add("application/vnd.sun.xml.calc", icon);
+ add("application/vnd.sun.xml.calc.template", icon);
+ add("application/x-kspread", icon);
+
+ // Document
+ icon = R.drawable.ic_doc_document;
+ add("application/vnd.oasis.opendocument.text", icon);
+ add("application/vnd.oasis.opendocument.text-master", icon);
+ add("application/vnd.oasis.opendocument.text-template", icon);
+ add("application/vnd.oasis.opendocument.text-web", icon);
+ add("application/vnd.stardivision.writer", icon);
+ add("application/vnd.stardivision.writer-global", icon);
+ add("application/vnd.sun.xml.writer", icon);
+ add("application/vnd.sun.xml.writer.global", icon);
+ add("application/vnd.sun.xml.writer.template", icon);
+ add("application/x-abiword", icon);
+ add("application/x-kword", icon);
+
+ // Video
+ icon = R.drawable.ic_doc_video;
+ add("application/x-quicktimeplayer", icon);
+ add("application/x-shockwave-flash", icon);
+
+ // Word
+ icon = R.drawable.ic_doc_word;
+ add("application/msword", icon);
+ add("application/vnd.openxmlformats-officedocument.wordprocessingml.document", icon);
+ add("application/vnd.openxmlformats-officedocument.wordprocessingml.template", icon);
+
+ // Excel
+ icon = R.drawable.ic_doc_excel;
+ add("application/vnd.ms-excel", icon);
+ add("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", icon);
+ add("application/vnd.openxmlformats-officedocument.spreadsheetml.template", icon);
+
+ // Powerpoint
+ icon = R.drawable.ic_doc_powerpoint;
+ add("application/vnd.ms-powerpoint", icon);
+ add("application/vnd.openxmlformats-officedocument.presentationml.presentation", icon);
+ add("application/vnd.openxmlformats-officedocument.presentationml.template", icon);
+ add("application/vnd.openxmlformats-officedocument.presentationml.slideshow", icon);
+ }
+
+ public static Drawable loadMimeIcon(Context context, String mimeType) {
+ if (DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType)) {
+ return context.getDrawable(R.drawable.ic_doc_folder);
+ }
+
+ // Look for exact match first
+ Integer resId = sMimeIcons.get(mimeType);
+ if (resId != null) {
+ return context.getDrawable(resId);
+ }
+
+ if (mimeType == null) {
+ // TODO: generic icon?
+ return null;
+ }
+
+ // Otherwise look for partial match
+ final String typeOnly = mimeType.split("/")[0];
+ if ("audio".equals(typeOnly)) {
+ return context.getDrawable(R.drawable.ic_doc_audio);
+ } else if ("image".equals(typeOnly)) {
+ return context.getDrawable(R.drawable.ic_doc_image);
+ } else if ("text".equals(typeOnly)) {
+ return context.getDrawable(R.drawable.ic_doc_text);
+ } else if ("video".equals(typeOnly)) {
+ return context.getDrawable(R.drawable.ic_doc_video);
+ } else {
+ return context.getDrawable(R.drawable.ic_doc_generic);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/util/NotificationColorUtil.java b/core/java/com/android/internal/util/NotificationColorUtil.java
index 48bcc09..4748e6f 100644
--- a/core/java/com/android/internal/util/NotificationColorUtil.java
+++ b/core/java/com/android/internal/util/NotificationColorUtil.java
@@ -271,7 +271,7 @@
* Finds a text color with sufficient contrast over bg that has the same hue as the original
* color, assuming it is for large text.
*/
- private static int ensureLargeTextContrast(int color, int bg) {
+ public static int ensureLargeTextContrast(int color, int bg) {
return findContrastColor(color, bg, true, 3);
}
@@ -341,6 +341,20 @@
}
/**
+ * Lighten a color by a specified value
+ * @param baseColor the base color to lighten
+ * @param amount the amount to lighten the color from 0 to 100. This corresponds to the L
+ * increase in the LAB color space.
+ * @return the lightened color
+ */
+ public static int lightenColor(int baseColor, int amount) {
+ final double[] result = ColorUtilsFromCompat.getTempDouble3Array();
+ ColorUtilsFromCompat.colorToLAB(baseColor, result);
+ result[0] = Math.min(100, result[0] + amount);
+ return ColorUtilsFromCompat.LABToColor(result[0], result[1], result[2]);
+ }
+
+ /**
* Framework copy of functions needed from android.support.v4.graphics.ColorUtils.
*/
private static class ColorUtilsFromCompat {
@@ -434,7 +448,7 @@
* Convert RGB components to its CIE Lab representative components.
*
* <ul>
- * <li>outLab[0] is L [0 ...1)</li>
+ * <li>outLab[0] is L [0 ...100)</li>
* <li>outLab[1] is a [-128...127)</li>
* <li>outLab[2] is b [-128...127)</li>
* </ul>
@@ -516,7 +530,7 @@
* 2° Standard Observer (1931).</p>
*
* <ul>
- * <li>outLab[0] is L [0 ...1)</li>
+ * <li>outLab[0] is L [0 ...100)</li>
* <li>outLab[1] is a [-128...127)</li>
* <li>outLab[2] is b [-128...127)</li>
* </ul>
@@ -634,7 +648,7 @@
: (XYZ_KAPPA * component + 16) / 116;
}
- private static double[] getTempDouble3Array() {
+ public static double[] getTempDouble3Array() {
double[] result = TEMP_ARRAY.get();
if (result == null) {
result = new double[3];
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index 39fd36b..a5a3dba 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -29,8 +29,8 @@
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
-import java.util.Iterator;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Vector;
/**
@@ -1642,7 +1642,7 @@
*
* Message is ignored if state machine has quit.
*/
- public final void sendMessage(int what) {
+ public void sendMessage(int what) {
// mSmHandler can be null if the state machine has quit.
SmHandler smh = mSmHandler;
if (smh == null) return;
@@ -1655,7 +1655,7 @@
*
* Message is ignored if state machine has quit.
*/
- public final void sendMessage(int what, Object obj) {
+ public void sendMessage(int what, Object obj) {
// mSmHandler can be null if the state machine has quit.
SmHandler smh = mSmHandler;
if (smh == null) return;
@@ -1668,7 +1668,7 @@
*
* Message is ignored if state machine has quit.
*/
- public final void sendMessage(int what, int arg1) {
+ public void sendMessage(int what, int arg1) {
// mSmHandler can be null if the state machine has quit.
SmHandler smh = mSmHandler;
if (smh == null) return;
@@ -1681,7 +1681,7 @@
*
* Message is ignored if state machine has quit.
*/
- public final void sendMessage(int what, int arg1, int arg2) {
+ public void sendMessage(int what, int arg1, int arg2) {
// mSmHandler can be null if the state machine has quit.
SmHandler smh = mSmHandler;
if (smh == null) return;
@@ -1694,7 +1694,7 @@
*
* Message is ignored if state machine has quit.
*/
- public final void sendMessage(int what, int arg1, int arg2, Object obj) {
+ public void sendMessage(int what, int arg1, int arg2, Object obj) {
// mSmHandler can be null if the state machine has quit.
SmHandler smh = mSmHandler;
if (smh == null) return;
@@ -1707,7 +1707,7 @@
*
* Message is ignored if state machine has quit.
*/
- public final void sendMessage(Message msg) {
+ public void sendMessage(Message msg) {
// mSmHandler can be null if the state machine has quit.
SmHandler smh = mSmHandler;
if (smh == null) return;
@@ -1720,7 +1720,7 @@
*
* Message is ignored if state machine has quit.
*/
- public final void sendMessageDelayed(int what, long delayMillis) {
+ public void sendMessageDelayed(int what, long delayMillis) {
// mSmHandler can be null if the state machine has quit.
SmHandler smh = mSmHandler;
if (smh == null) return;
@@ -1733,7 +1733,7 @@
*
* Message is ignored if state machine has quit.
*/
- public final void sendMessageDelayed(int what, Object obj, long delayMillis) {
+ public void sendMessageDelayed(int what, Object obj, long delayMillis) {
// mSmHandler can be null if the state machine has quit.
SmHandler smh = mSmHandler;
if (smh == null) return;
@@ -1746,7 +1746,7 @@
*
* Message is ignored if state machine has quit.
*/
- public final void sendMessageDelayed(int what, int arg1, long delayMillis) {
+ public void sendMessageDelayed(int what, int arg1, long delayMillis) {
// mSmHandler can be null if the state machine has quit.
SmHandler smh = mSmHandler;
if (smh == null) return;
@@ -1759,7 +1759,7 @@
*
* Message is ignored if state machine has quit.
*/
- public final void sendMessageDelayed(int what, int arg1, int arg2, long delayMillis) {
+ public void sendMessageDelayed(int what, int arg1, int arg2, long delayMillis) {
// mSmHandler can be null if the state machine has quit.
SmHandler smh = mSmHandler;
if (smh == null) return;
@@ -1772,7 +1772,7 @@
*
* Message is ignored if state machine has quit.
*/
- public final void sendMessageDelayed(int what, int arg1, int arg2, Object obj,
+ public void sendMessageDelayed(int what, int arg1, int arg2, Object obj,
long delayMillis) {
// mSmHandler can be null if the state machine has quit.
SmHandler smh = mSmHandler;
@@ -1786,7 +1786,7 @@
*
* Message is ignored if state machine has quit.
*/
- public final void sendMessageDelayed(Message msg, long delayMillis) {
+ public void sendMessageDelayed(Message msg, long delayMillis) {
// mSmHandler can be null if the state machine has quit.
SmHandler smh = mSmHandler;
if (smh == null) return;
diff --git a/core/java/com/android/internal/util/WakeupMessage.java b/core/java/com/android/internal/util/WakeupMessage.java
index 2653745..7d222c7 100644
--- a/core/java/com/android/internal/util/WakeupMessage.java
+++ b/core/java/com/android/internal/util/WakeupMessage.java
@@ -45,24 +45,32 @@
protected final String mCmdName;
@VisibleForTesting
protected final int mCmd, mArg1, mArg2;
+ @VisibleForTesting
+ protected final Object mObj;
private boolean mScheduled;
public WakeupMessage(Context context, Handler handler,
- String cmdName, int cmd, int arg1, int arg2) {
+ String cmdName, int cmd, int arg1, int arg2, Object obj) {
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mHandler = handler;
mCmdName = cmdName;
mCmd = cmd;
mArg1 = arg1;
mArg2 = arg2;
+ mObj = obj;
}
public WakeupMessage(Context context, Handler handler, String cmdName, int cmd, int arg1) {
- this(context, handler, cmdName, cmd, arg1, 0);
+ this(context, handler, cmdName, cmd, arg1, 0, null);
+ }
+
+ public WakeupMessage(Context context, Handler handler,
+ String cmdName, int cmd, int arg1, int arg2) {
+ this(context, handler, cmdName, cmd, arg1, arg2, null);
}
public WakeupMessage(Context context, Handler handler, String cmdName, int cmd) {
- this(context, handler, cmdName, cmd, 0, 0);
+ this(context, handler, cmdName, cmd, 0, 0, null);
}
/**
@@ -99,7 +107,7 @@
mScheduled = false;
}
if (stillScheduled) {
- Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2);
+ Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2, mObj);
mHandler.handleMessage(msg);
msg.recycle();
}
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 59b4b35..644c7e9 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -33,6 +33,7 @@
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionInspector;
import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
+import android.view.inputmethod.InputContentInfo;
public abstract class IInputConnectionWrapper extends IInputContext.Stub {
static final String TAG = "IInputConnectionWrapper";
@@ -61,6 +62,7 @@
private static final int DO_CLEAR_META_KEY_STATES = 130;
private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140;
private static final int DO_CLOSE_CONNECTION = 150;
+ private static final int DO_COMMIT_CONTENT = 160;
@GuardedBy("mLock")
@Nullable
@@ -241,6 +243,12 @@
dispatchMessage(obtainMessage(DO_CLOSE_CONNECTION));
}
+ public void commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts,
+ int seq, IInputContextCallback callback) {
+ dispatchMessage(obtainMessageIOOSC(DO_COMMIT_CONTENT, flags, inputContentInfo, opts, seq,
+ callback));
+ }
+
void dispatchMessage(Message msg) {
// If we are calling this from the main thread, then we can call
// right through. Otherwise, we need to send the message to the
@@ -552,6 +560,41 @@
}
return;
}
+ case DO_COMMIT_CONTENT: {
+ final int flags = msg.arg1;
+ final boolean grantUriPermission =
+ (flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0;
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "commitContent on inactive InputConnection");
+ args.callback.setCommitContentResult(false, args.seq);
+ return;
+ }
+ final InputContentInfo inputContentInfo = (InputContentInfo) args.arg1;
+ if (inputContentInfo == null || !inputContentInfo.validate()) {
+ Log.w(TAG, "commitContent with invalid inputContentInfo="
+ + inputContentInfo);
+ args.callback.setCommitContentResult(false, args.seq);
+ return;
+ }
+ if (grantUriPermission) {
+ inputContentInfo.requestPermission();
+ }
+ final boolean result =
+ ic.commitContent(inputContentInfo, flags, (Bundle) args.arg2);
+ // If this request is not handled, then there is no reason to keep the URI
+ // permission.
+ if (grantUriPermission && !result) {
+ inputContentInfo.releasePermission();
+ }
+ args.callback.setCommitContentResult(result, args.seq);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Got RemoteException calling commitContent", e);
+ }
+ return;
+ }
}
Log.w(TAG, "Unhandled message code: " + msg.what);
}
@@ -582,12 +625,14 @@
return mH.obtainMessage(what, arg1, arg2, args);
}
- Message obtainMessageOSC(int what, Object arg1, int seq, IInputContextCallback callback) {
+ Message obtainMessageIOOSC(int what, int arg1, Object objArg1, Object objArg2, int seq,
+ IInputContextCallback callback) {
SomeArgs args = new SomeArgs();
- args.arg1 = arg1;
+ args.arg1 = objArg1;
+ args.arg2 = objArg2;
args.callback = callback;
args.seq = seq;
- return mH.obtainMessage(what, 0, 0, args);
+ return mH.obtainMessage(what, arg1, 0, args);
}
Message obtainMessageIOSC(int what, int arg1, Object arg2, int seq,
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index f7ec420..728c557 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -21,6 +21,7 @@
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputContentInfo;
import com.android.internal.view.IInputContextCallback;
@@ -74,6 +75,9 @@
void getSelectedText(int flags, int seq, IInputContextCallback callback);
- void requestUpdateCursorAnchorInfo(in int cursorUpdateMode, int seq,
+ void requestUpdateCursorAnchorInfo(int cursorUpdateMode, int seq,
+ IInputContextCallback callback);
+
+ void commitContent(in InputContentInfo inputContentInfo, int flags, in Bundle opts, int sec,
IInputContextCallback callback);
}
diff --git a/core/java/com/android/internal/view/IInputContextCallback.aidl b/core/java/com/android/internal/view/IInputContextCallback.aidl
index 54ea306..0f40a83 100644
--- a/core/java/com/android/internal/view/IInputContextCallback.aidl
+++ b/core/java/com/android/internal/view/IInputContextCallback.aidl
@@ -28,4 +28,5 @@
void setExtractedText(in ExtractedText extractedText, int seq);
void setSelectedText(CharSequence selectedText, int seq);
void setRequestUpdateCursorAnchorInfoResult(boolean result, int seq);
+ void setCommitContentResult(boolean result, int seq);
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index cb7c3bf..9e4b43b 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -16,11 +16,13 @@
package com.android.internal.view;
+import android.net.Uri;
import android.os.ResultReceiver;
import android.text.style.SuggestionSpan;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.EditorInfo;
+import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.view.InputBindResult;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
@@ -81,5 +83,8 @@
int getInputMethodWindowVisibleHeight();
void clearLastInputMethodWindowForTransition(in IBinder token);
+ IInputContentUriToken createInputContentUriToken(in IBinder token, in Uri contentUri,
+ in String packageName);
+
oneway void notifyUserAction(int sequenceNumber);
}
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index f9884d8..9a09dcc 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -16,6 +16,8 @@
package com.android.internal.view;
+import android.annotation.NonNull;
+import android.inputmethodservice.AbstractInputMethodService;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
@@ -29,10 +31,16 @@
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionInspector;
import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
+import android.view.inputmethod.InputContentInfo;
+
+import java.lang.ref.WeakReference;
public class InputConnectionWrapper implements InputConnection {
private static final int MAX_WAIT_TIME_MILLIS = 2000;
private final IInputContext mIInputContext;
+ @NonNull
+ private final WeakReference<AbstractInputMethodService> mInputMethodService;
+
@MissingMethodFlags
private final int mMissingMethods;
@@ -46,7 +54,8 @@
public ExtractedText mExtractedText;
public int mCursorCapsMode;
public boolean mRequestUpdateCursorAnchorInfoResult;
-
+ public boolean mCommitContentResult;
+
// A 'pool' of one InputContextCallback. Each ICW request will attempt to gain
// exclusive access to this object.
private static InputContextCallback sInstance = new InputContextCallback();
@@ -172,6 +181,19 @@
}
}
+ public void setCommitContentResult(boolean result, int seq) {
+ synchronized (this) {
+ if (seq == mSeq) {
+ mCommitContentResult = result;
+ mHaveValue = true;
+ notifyAll();
+ } else {
+ Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
+ + ") in setCommitContentResult, ignoring.");
+ }
+ }
+ }
+
/**
* Waits for a result for up to {@link #MAX_WAIT_TIME_MILLIS} milliseconds.
*
@@ -195,8 +217,10 @@
}
}
- public InputConnectionWrapper(IInputContext inputContext,
- @MissingMethodFlags final int missingMethods) {
+ public InputConnectionWrapper(
+ @NonNull WeakReference<AbstractInputMethodService> inputMethodService,
+ IInputContext inputContext, @MissingMethodFlags final int missingMethods) {
+ mInputMethodService = inputMethodService;
mIInputContext = inputContext;
mMissingMethods = missingMethods;
}
@@ -491,6 +515,37 @@
// Nothing should happen when called from input method.
}
+ public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
+ boolean result = false;
+ if (isMethodMissing(MissingMethodFlags.COMMIT_CONTENT)) {
+ // This method is not implemented.
+ return false;
+ }
+ try {
+ if ((flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
+ final AbstractInputMethodService inputMethodService = mInputMethodService.get();
+ if (inputMethodService == null) {
+ // This basically should not happen, because it's the the caller of this method.
+ return false;
+ }
+ inputMethodService.exposeContent(inputContentInfo, this);
+ }
+
+ InputContextCallback callback = InputContextCallback.getInstance();
+ mIInputContext.commitContent(inputContentInfo, flags, opts, callback.mSeq, callback);
+ synchronized (callback) {
+ callback.waitForResultLocked();
+ if (callback.mHaveValue) {
+ result = callback.mCommitContentResult;
+ }
+ }
+ callback.dispose();
+ } catch (RemoteException e) {
+ return false;
+ }
+ return result;
+ }
+
private boolean isMethodMissing(@MissingMethodFlags final int methodFlag) {
return (mMissingMethods & methodFlag) == methodFlag;
}
diff --git a/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java b/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
index 9e2cbdb..d28ab07 100644
--- a/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
+++ b/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
@@ -30,6 +30,8 @@
@HasNativeInterpolator
public class FallbackLUTInterpolator implements NativeInterpolatorFactory, TimeInterpolator {
+ // If the duration of an animation is more than 300 frames, we cap the sample size to 300.
+ private static final int MAX_SAMPLE_POINTS = 300;
private TimeInterpolator mSourceInterpolator;
private final float mLut[];
@@ -47,6 +49,7 @@
int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS);
// We need 2 frame values as the minimal.
int numAnimFrames = Math.max(2, (int) Math.ceil(((double) duration) / animIntervalMs));
+ numAnimFrames = Math.min(numAnimFrames, MAX_SAMPLE_POINTS);
float values[] = new float[numAnimFrames];
float lastFrame = numAnimFrames - 1;
for (int i = 0; i < numAnimFrames; i++) {
diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
index ddf3a76..69e974c 100644
--- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
@@ -366,7 +366,7 @@
final int menuWidth = measureIndividualMenuWidth(adapter, null, mContext, mMenuMaxWidth);
final MenuPopupWindow popupWindow = createPopupWindow();
popupWindow.setAdapter(adapter);
- popupWindow.setWidth(menuWidth);
+ popupWindow.setContentWidth(menuWidth);
popupWindow.setDropDownGravity(mDropDownGravity);
final CascadingMenuInfo parentInfo;
diff --git a/core/java/com/android/internal/widget/CachingIconView.java b/core/java/com/android/internal/widget/CachingIconView.java
new file mode 100644
index 0000000..293b77b
--- /dev/null
+++ b/core/java/com/android/internal/widget/CachingIconView.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.widget;
+
+import android.annotation.DrawableRes;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.RemotableViewMethod;
+import android.widget.ImageView;
+import android.widget.RemoteViews;
+
+import libcore.util.Objects;
+
+/**
+ * An ImageView for displaying an Icon. Avoids reloading the Icon when possible.
+ */
+@RemoteViews.RemoteView
+public class CachingIconView extends ImageView {
+
+ private String mLastPackage;
+ private int mLastResId;
+ private boolean mInternalSetDrawable;
+
+ public CachingIconView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ @RemotableViewMethod(asyncImpl="setImageIconAsync")
+ public void setImageIcon(@Nullable Icon icon) {
+ if (!testAndSetCache(icon)) {
+ mInternalSetDrawable = true;
+ // This calls back to setImageDrawable, make sure we don't clear the cache there.
+ super.setImageIcon(icon);
+ mInternalSetDrawable = false;
+ }
+ }
+
+ @Override
+ public Runnable setImageIconAsync(@Nullable Icon icon) {
+ resetCache();
+ return super.setImageIconAsync(icon);
+ }
+
+ @Override
+ @RemotableViewMethod(asyncImpl="setImageResourceAsync")
+ public void setImageResource(@DrawableRes int resId) {
+ if (!testAndSetCache(resId)) {
+ mInternalSetDrawable = true;
+ // This calls back to setImageDrawable, make sure we don't clear the cache there.
+ super.setImageResource(resId);
+ mInternalSetDrawable = false;
+ }
+ }
+
+ @Override
+ public Runnable setImageResourceAsync(@DrawableRes int resId) {
+ resetCache();
+ return super.setImageResourceAsync(resId);
+ }
+
+ @Override
+ @RemotableViewMethod(asyncImpl="setImageURIAsync")
+ public void setImageURI(@Nullable Uri uri) {
+ resetCache();
+ super.setImageURI(uri);
+ }
+
+ @Override
+ public Runnable setImageURIAsync(@Nullable Uri uri) {
+ resetCache();
+ return super.setImageURIAsync(uri);
+ }
+
+ @Override
+ public void setImageDrawable(@Nullable Drawable drawable) {
+ if (!mInternalSetDrawable) {
+ // Only clear the cache if we were externally called.
+ resetCache();
+ }
+ super.setImageDrawable(drawable);
+ }
+
+ @Override
+ @RemotableViewMethod
+ public void setImageBitmap(Bitmap bm) {
+ resetCache();
+ super.setImageBitmap(bm);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ resetCache();
+ }
+
+ /**
+ * @return true if the currently set image is the same as {@param icon}
+ */
+ private synchronized boolean testAndSetCache(Icon icon) {
+ if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
+ String iconPackage = normalizeIconPackage(icon);
+
+ boolean isCached = mLastResId != 0
+ && icon.getResId() == mLastResId
+ && Objects.equal(iconPackage, mLastPackage);
+
+ mLastPackage = iconPackage;
+ mLastResId = icon.getResId();
+
+ return isCached;
+ } else {
+ resetCache();
+ return false;
+ }
+ }
+
+ /**
+ * @return true if the currently set image is the same as {@param resId}
+ */
+ private synchronized boolean testAndSetCache(int resId) {
+ boolean isCached;
+ if (resId == 0 || mLastResId == 0) {
+ isCached = false;
+ } else {
+ isCached = resId == mLastResId && null == mLastPackage;
+ }
+ mLastPackage = null;
+ mLastResId = resId;
+ return isCached;
+ }
+
+ /**
+ * Returns the normalized package name of {@param icon}.
+ * @return null if icon is null or if the icons package is null, empty or matches the current
+ * context. Otherwise returns the icon's package context.
+ */
+ private String normalizeIconPackage(Icon icon) {
+ if (icon == null) {
+ return null;
+ }
+
+ String pkg = icon.getResPackage();
+ if (TextUtils.isEmpty(pkg)) {
+ return null;
+ }
+ if (pkg.equals(mContext.getPackageName())) {
+ return null;
+ }
+ return pkg;
+ }
+
+ private synchronized void resetCache() {
+ mLastResId = 0;
+ mLastPackage = null;
+ }
+}
diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java
index e59d7ba..fc68b84 100644
--- a/core/java/com/android/internal/widget/DecorCaptionView.java
+++ b/core/java/com/android/internal/widget/DecorCaptionView.java
@@ -244,6 +244,11 @@
return mTouchDispatchList;
}
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return false;
+ }
+
private boolean passedSlop(int x, int y) {
return Math.abs(x - mTouchDownX) > mDragSlop || Math.abs(y - mTouchDownY) > mDragSlop;
}
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 594581a..6604208 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -737,7 +737,7 @@
protected void applyTransformation(float interpolatedTime, Transformation t) {
int deltaWidth = (int) (interpolatedTime * (targetWidth - startWidth));
setWidth(mContentContainer, startWidth + deltaWidth);
- if (isRTL()) {
+ if (isInRTLMode()) {
mContentContainer.setX(left);
// Lock the panels in place.
@@ -766,7 +766,7 @@
}
};
final float overflowButtonStartX = mOverflowButton.getX();
- final float overflowButtonTargetX = isRTL() ?
+ final float overflowButtonTargetX = isInRTLMode() ?
overflowButtonStartX + targetWidth - mOverflowButton.getWidth() :
overflowButtonStartX - targetWidth + mOverflowButton.getWidth();
Animation overflowButtonAnimation = new Animation() {
@@ -774,7 +774,7 @@
protected void applyTransformation(float interpolatedTime, Transformation t) {
float overflowButtonX = overflowButtonStartX
+ interpolatedTime * (overflowButtonTargetX - overflowButtonStartX);
- float deltaContainerWidth = isRTL() ?
+ float deltaContainerWidth = isInRTLMode() ?
0 :
mContentContainer.getWidth() - startWidth;
float actualOverflowButtonX = overflowButtonX + deltaContainerWidth;
@@ -812,7 +812,7 @@
protected void applyTransformation(float interpolatedTime, Transformation t) {
int deltaWidth = (int) (interpolatedTime * (targetWidth - startWidth));
setWidth(mContentContainer, startWidth + deltaWidth);
- if (isRTL()) {
+ if (isInRTLMode()) {
mContentContainer.setX(left);
// Lock the panels in place.
@@ -843,7 +843,7 @@
}
};
final float overflowButtonStartX = mOverflowButton.getX();
- final float overflowButtonTargetX = isRTL() ?
+ final float overflowButtonTargetX = isInRTLMode() ?
overflowButtonStartX - startWidth + mOverflowButton.getWidth() :
overflowButtonStartX + startWidth - mOverflowButton.getWidth();
Animation overflowButtonAnimation = new Animation() {
@@ -851,7 +851,7 @@
protected void applyTransformation(float interpolatedTime, Transformation t) {
float overflowButtonX = overflowButtonStartX
+ interpolatedTime * (overflowButtonTargetX - overflowButtonStartX);
- float deltaContainerWidth = isRTL() ?
+ float deltaContainerWidth = isInRTLMode() ?
0 :
mContentContainer.getWidth() - startWidth;
float actualOverflowButtonX = overflowButtonX + deltaContainerWidth;
@@ -903,7 +903,7 @@
R.string.floating_toolbar_close_overflow_description));
// Update x-coordinates depending on RTL state.
- if (isRTL()) {
+ if (isInRTLMode()) {
mContentContainer.setX(mMarginHorizontal); // align left
mMainPanel.setX(0); // align left
mOverflowButton.setX( // align right
@@ -947,7 +947,7 @@
if (hasOverflow()) {
// Update x-coordinates depending on RTL state.
- if (isRTL()) {
+ if (isInRTLMode()) {
mContentContainer.setX(mMarginHorizontal); // align left
mMainPanel.setX(0); // align left
mOverflowButton.setX(0); // align left
@@ -1087,9 +1087,10 @@
viewTreeObserver.addOnComputeInternalInsetsListener(mInsetsComputer);
}
- private boolean isRTL() {
- return mContext.getResources().getConfiguration().getLayoutDirection()
- == View.LAYOUT_DIRECTION_RTL;
+ private boolean isInRTLMode() {
+ return mContext.getApplicationInfo().hasRtlSupport()
+ && mContext.getResources().getConfiguration().getLayoutDirection()
+ == View.LAYOUT_DIRECTION_RTL;
}
private boolean hasOverflow() {
@@ -1203,7 +1204,7 @@
// The positioning of contents in RTL is wrong when the view is first rendered.
// Hide the view and post a runnable to recalculate positions and render the view.
// TODO: Investigate why this happens and fix.
- if (isRTL()) {
+ if (isInRTLMode()) {
mContentContainer.setAlpha(0);
mContentContainer.post(mPreparePopupContentRTLHelper);
}
diff --git a/telecomm/java/android/telecom/ParcelableCallAnalytics.aidl b/core/java/com/android/internal/widget/ICheckCredentialProgressCallback.aidl
similarity index 67%
copy from telecomm/java/android/telecom/ParcelableCallAnalytics.aidl
copy to core/java/com/android/internal/widget/ICheckCredentialProgressCallback.aidl
index b7e78d1..79717cf 100644
--- a/telecomm/java/android/telecom/ParcelableCallAnalytics.aidl
+++ b/core/java/com/android/internal/widget/ICheckCredentialProgressCallback.aidl
@@ -1,11 +1,11 @@
/*
- * Copyright 2016, The Android Open Source Project
+ * 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
+ * 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,
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package android.telecom;
+package com.android.internal.widget;
-/**
- * {@hide}
- */
-parcelable ParcelableCallAnalytics;
+/** {@hide} */
+oneway interface ICheckCredentialProgressCallback {
+ void onCredentialVerified();
+}
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 05b839d..9fa558e 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -17,6 +17,7 @@
package com.android.internal.widget;
import android.app.trust.IStrongAuthTracker;
+import com.android.internal.widget.ICheckCredentialProgressCallback;
import com.android.internal.widget.VerifyCredentialResponse;
/** {@hide} */
@@ -29,10 +30,12 @@
String getString(in String key, in String defaultValue, in int userId);
void setLockPattern(in String pattern, in String savedPattern, int userId);
void resetKeyStore(int userId);
- VerifyCredentialResponse checkPattern(in String pattern, int userId);
+ VerifyCredentialResponse checkPattern(in String pattern, int userId,
+ in ICheckCredentialProgressCallback progressCallback);
VerifyCredentialResponse verifyPattern(in String pattern, long challenge, int userId);
void setLockPassword(in String password, in String savedPassword, int userId);
- VerifyCredentialResponse checkPassword(in String password, int userId);
+ VerifyCredentialResponse checkPassword(in String password, int userId,
+ in ICheckCredentialProgressCallback progressCallback);
VerifyCredentialResponse verifyPassword(in String password, long challenge, int userId);
VerifyCredentialResponse verifyTiedProfileChallenge(String password, boolean isPattern, long challenge, int userId);
boolean checkVoldPassword(int userId);
diff --git a/core/java/com/android/internal/widget/LockPatternChecker.java b/core/java/com/android/internal/widget/LockPatternChecker.java
index 713f56f..df9b0dd 100644
--- a/core/java/com/android/internal/widget/LockPatternChecker.java
+++ b/core/java/com/android/internal/widget/LockPatternChecker.java
@@ -14,6 +14,13 @@
* Interface for a callback to be invoked after security check.
*/
public interface OnCheckCallback {
+
+ /**
+ * Invoked as soon as possible we know that the credentials match. This will be called
+ * earlier than {@link #onChecked} but only if the credentials match.
+ */
+ default void onEarlyMatched() {}
+
/**
* Invoked when a security check is finished.
*
@@ -92,7 +99,7 @@
@Override
protected Boolean doInBackground(Void... args) {
try {
- return utils.checkPattern(pattern, userId);
+ return utils.checkPattern(pattern, userId, callback::onEarlyMatched);
} catch (RequestThrottledException ex) {
mThrottleTimeout = ex.getTimeoutMs();
return false;
@@ -199,7 +206,7 @@
@Override
protected Boolean doInBackground(Void... args) {
try {
- return utils.checkPassword(password, userId);
+ return utils.checkPassword(password, userId, callback::onEarlyMatched);
} catch (RequestThrottledException ex) {
mThrottleTimeout = ex.getTimeoutMs();
return false;
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 7f2f740..479b3b7 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -17,6 +17,7 @@
package com.android.internal.widget;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
import android.app.trust.IStrongAuthTracker;
import android.app.trust.TrustManager;
@@ -32,7 +33,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.IMountService;
@@ -149,6 +149,7 @@
private DevicePolicyManager mDevicePolicyManager;
private ILockSettings mLockSettingsService;
private UserManager mUserManager;
+ private final Handler mHandler;
/**
* Use {@link TrustManager#isTrustUsuallyManaged(int)}.
@@ -230,6 +231,9 @@
public LockPatternUtils(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
+
+ Looper looper = Looper.myLooper();
+ mHandler = looper != null ? new Handler(looper) : null;
}
private ILockSettings getLockSettings() {
@@ -284,7 +288,6 @@
public void reportFailedPasswordAttempt(int userId) {
getDevicePolicyManager().reportFailedPasswordAttempt(userId);
getTrustManager().reportUnlockAttempt(false /* authenticated */, userId);
- requireStrongAuth(StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_WRONG_CREDENTIAL, userId);
}
public void reportSuccessfulPasswordAttempt(int userId) {
@@ -341,10 +344,23 @@
*/
public boolean checkPattern(List<LockPatternView.Cell> pattern, int userId)
throws RequestThrottledException {
+ return checkPattern(pattern, userId, null /* progressCallback */);
+ }
+
+ /**
+ * Check to see if a pattern matches the saved pattern. If no pattern exists,
+ * always returns true.
+ * @param pattern The pattern to check.
+ * @return Whether the pattern matches the stored one.
+ */
+ public boolean checkPattern(List<LockPatternView.Cell> pattern, int userId,
+ @Nullable CheckCredentialProgressCallback progressCallback)
+ throws RequestThrottledException {
throwIfCalledOnMainThread();
try {
VerifyCredentialResponse response =
- getLockSettings().checkPattern(patternToString(pattern), userId);
+ getLockSettings().checkPattern(patternToString(pattern), userId,
+ wrapCallback(progressCallback));
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
return true;
@@ -423,10 +439,22 @@
* @return Whether the password matches the stored one.
*/
public boolean checkPassword(String password, int userId) throws RequestThrottledException {
+ return checkPassword(password, userId, null /* progressCallback */);
+ }
+
+ /**
+ * Check to see if a password matches the saved password. If no password exists,
+ * always returns true.
+ * @param password The password to check.
+ * @return Whether the password matches the stored one.
+ */
+ public boolean checkPassword(String password, int userId,
+ @Nullable CheckCredentialProgressCallback progressCallback)
+ throws RequestThrottledException {
throwIfCalledOnMainThread();
try {
VerifyCredentialResponse response =
- getLockSettings().checkPassword(password, userId);
+ getLockSettings().checkPassword(password, userId, wrapCallback(progressCallback));
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
return true;
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
@@ -1475,6 +1503,37 @@
return (getStrongAuthForUser(userId) & ~StrongAuthTracker.ALLOWING_FINGERPRINT) == 0;
}
+ private ICheckCredentialProgressCallback wrapCallback(
+ final CheckCredentialProgressCallback callback) {
+ if (callback == null) {
+ return null;
+ } else {
+ if (mHandler == null) {
+ throw new IllegalStateException("Must construct LockPatternUtils on a looper thread"
+ + " to use progress callbacks.");
+ }
+ return new ICheckCredentialProgressCallback.Stub() {
+
+ @Override
+ public void onCredentialVerified() throws RemoteException {
+ mHandler.post(callback::onEarlyMatched);
+ }
+ };
+ }
+ }
+
+ /**
+ * Callback to be notified about progress when checking credentials.
+ */
+ public interface CheckCredentialProgressCallback {
+
+ /**
+ * Called as soon as possible when we know that the credentials match but the user hasn't
+ * been fully unlocked.
+ */
+ void onEarlyMatched();
+ }
+
/**
* Tracks the global strong authentication state.
*/
@@ -1484,7 +1543,8 @@
value = { STRONG_AUTH_NOT_REQUIRED,
STRONG_AUTH_REQUIRED_AFTER_BOOT,
STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW,
- SOME_AUTH_REQUIRED_AFTER_USER_REQUEST})
+ SOME_AUTH_REQUIRED_AFTER_USER_REQUEST,
+ STRONG_AUTH_REQUIRED_AFTER_LOCKOUT})
@Retention(RetentionPolicy.SOURCE)
public @interface StrongAuthFlags {}
@@ -1515,13 +1575,12 @@
public static final int STRONG_AUTH_REQUIRED_AFTER_LOCKOUT = 0x8;
/**
- * Some authentication is required because the user has entered a wrong credential.
+ * Strong auth flags that do not prevent fingerprint from being accepted as auth.
+ *
+ * If any other flags are set, fingerprint is disabled.
*/
- public static final int SOME_AUTH_REQUIRED_AFTER_WRONG_CREDENTIAL = 0x10;
-
private static final int ALLOWING_FINGERPRINT = STRONG_AUTH_NOT_REQUIRED
- | SOME_AUTH_REQUIRED_AFTER_USER_REQUEST
- | SOME_AUTH_REQUIRED_AFTER_WRONG_CREDENTIAL;
+ | SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
private final SparseIntArray mStrongAuthRequiredForUser = new SparseIntArray();
private final H mHandler;
diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java
index 9dd118c..073aac5 100644
--- a/core/java/com/android/internal/widget/NotificationActionListLayout.java
+++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java
@@ -17,11 +17,13 @@
package com.android.internal.widget;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Pair;
import android.view.Gravity;
+import android.view.RemotableViewMethod;
import android.view.View;
-import android.view.ViewGroup;
+import android.widget.LinearLayout;
import android.widget.RemoteViews;
import android.widget.TextView;
@@ -33,11 +35,14 @@
* the remaining available width, and the last action consumes the remaining space.
*/
@RemoteViews.RemoteView
-public class NotificationActionListLayout extends ViewGroup {
+public class NotificationActionListLayout extends LinearLayout {
private int mTotalWidth = 0;
private ArrayList<Pair<Integer, TextView>> mMeasureOrderTextViews = new ArrayList<>();
private ArrayList<View> mMeasureOrderOther = new ArrayList<>();
+ private boolean mMeasureLinearly;
+ private int mDefaultPaddingEnd;
+ private Drawable mDefaultBackground;
public NotificationActionListLayout(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -45,6 +50,10 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (mMeasureLinearly) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ return;
+ }
final int N = getChildCount();
int textViews = 0;
int otherViews = 0;
@@ -186,6 +195,10 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (mMeasureLinearly) {
+ super.onLayout(changed, left, top, right, bottom);
+ return;
+ }
final boolean isLayoutRtl = isLayoutRtl();
final int paddingTop = mPaddingTop;
@@ -241,26 +254,24 @@
}
@Override
- public LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new MarginLayoutParams(getContext(), attrs);
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mDefaultPaddingEnd = getPaddingEnd();
+ mDefaultBackground = getBackground();
}
- @Override
- protected LayoutParams generateDefaultLayoutParams() {
- return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
- }
-
- @Override
- protected LayoutParams generateLayoutParams(LayoutParams p) {
- if (p instanceof MarginLayoutParams) {
- return new MarginLayoutParams((MarginLayoutParams)p);
- }
- return new MarginLayoutParams(p);
- }
-
- @Override
- protected boolean checkLayoutParams(LayoutParams p) {
- return p instanceof MarginLayoutParams;
+ /**
+ * Set whether the list is in a mode where some actions are emphasized. This will trigger an
+ * equal measuring where all actions are full height and change a few parameters like
+ * the padding.
+ */
+ @RemotableViewMethod
+ public void setEmphasizedMode(boolean emphasizedMode) {
+ mMeasureLinearly = emphasizedMode;
+ setPaddingRelative(getPaddingStart(), getPaddingTop(),
+ emphasizedMode ? 0 : mDefaultPaddingEnd, getPaddingBottom());
+ setBackground(emphasizedMode ? null : mDefaultBackground);
+ requestLayout();
}
public static final Comparator<Pair<Integer, TextView>> MEASURE_ORDER_COMPARATOR
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index 277fafd..d5b6def 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -55,34 +55,7 @@
import java.util.Comparator;
/**
- * Layout manager that allows the user to flip left and right
- * through pages of data. You supply an implementation of a
- * {@link android.support.v4.view.PagerAdapter} to generate the pages that the view shows.
- *
- * <p>Note this class is currently under early design and
- * development. The API will likely change in later updates of
- * the compatibility library, requiring changes to the source code
- * of apps when they are compiled against the newer version.</p>
- *
- * <p>ViewPager is most often used in conjunction with {@link android.app.Fragment},
- * which is a convenient way to supply and manage the lifecycle of each page.
- * There are standard adapters implemented for using fragments with the ViewPager,
- * which cover the most common use cases. These are
- * {@link android.support.v4.app.FragmentPagerAdapter} and
- * {@link android.support.v4.app.FragmentStatePagerAdapter}; each of these
- * classes have simple code showing how to build a full user interface
- * with them.
- *
- * <p>For more information about how to use ViewPager, read <a
- * href="{@docRoot}training/implementing-navigation/lateral.html">Creating Swipe Views with
- * Tabs</a>.</p>
- *
- * <p>Below is a more complicated example of ViewPager, using it in conjunction
- * with {@link android.app.ActionBar} tabs. You can find other examples of using
- * ViewPager in the API 4+ Support Demos and API 13+ Support Demos sample code.
- *
- * {@sample development/samples/Support13Demos/src/com/example/android/supportv13/app/ActionBarTabsPager.java
- * complete}
+ * Framework copy of the support-v4 ViewPager class.
*/
public class ViewPager extends ViewGroup {
private static final String TAG = "ViewPager";
diff --git a/core/java/com/android/internal/widget/WatchHeaderListView.java b/core/java/com/android/internal/widget/WatchHeaderListView.java
new file mode 100644
index 0000000..3d32d86
--- /dev/null
+++ b/core/java/com/android/internal/widget/WatchHeaderListView.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget;
+
+import android.annotation.IdRes;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.HeaderViewListAdapter;
+
+import java.util.ArrayList;
+
+import com.android.internal.util.Predicate;
+
+public class WatchHeaderListView extends ListView {
+ private View mTopPanel;
+
+ public WatchHeaderListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public WatchHeaderListView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public WatchHeaderListView(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected HeaderViewListAdapter wrapHeaderListAdapterInternal(
+ ArrayList<ListView.FixedViewInfo> headerViewInfos,
+ ArrayList<ListView.FixedViewInfo> footerViewInfos,
+ ListAdapter adapter) {
+ return new WatchHeaderListAdapter(headerViewInfos, footerViewInfos, adapter);
+ }
+
+ @Override
+ public void addView(View child, ViewGroup.LayoutParams params) {
+ if (mTopPanel == null) {
+ setTopPanel(child);
+ } else {
+ throw new IllegalStateException("WatchHeaderListView can host only one header");
+ }
+ }
+
+ public void setTopPanel(View v) {
+ mTopPanel = v;
+ wrapAdapterIfNecessary();
+ }
+
+ @Override
+ public void setAdapter(ListAdapter adapter) {
+ super.setAdapter(adapter);
+ wrapAdapterIfNecessary();
+ }
+
+ @Override
+ protected View findViewTraversal(@IdRes int id) {
+ View v = super.findViewTraversal(id);
+ if (v == null && mTopPanel != null && !mTopPanel.isRootNamespace()) {
+ return mTopPanel.findViewById(id);
+ }
+ return v;
+ }
+
+ @Override
+ protected View findViewWithTagTraversal(Object tag) {
+ View v = super.findViewWithTagTraversal(tag);
+ if (v == null && mTopPanel != null && !mTopPanel.isRootNamespace()) {
+ return mTopPanel.findViewWithTag(tag);
+ }
+ return v;
+ }
+
+ @Override
+ protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
+ View v = super.findViewByPredicateTraversal(predicate, childToSkip);
+ if (v == null && mTopPanel != null && mTopPanel != childToSkip
+ && !mTopPanel.isRootNamespace()) {
+ return mTopPanel.findViewByPredicate(predicate);
+ }
+ return v;
+ }
+
+ @Override
+ public int getHeaderViewsCount() {
+ return mTopPanel == null ? super.getHeaderViewsCount() : super.getHeaderViewsCount() + 1;
+ }
+
+ private void wrapAdapterIfNecessary() {
+ ListAdapter adapter = getAdapter();
+ if (adapter != null && mTopPanel != null) {
+ if (!(adapter instanceof WatchHeaderListAdapter)) {
+ wrapHeaderListAdapterInternal();
+ }
+
+ ((WatchHeaderListAdapter) getAdapter()).setTopPanel(mTopPanel);
+ dispatchDataSetObserverOnChangedInternal();
+ }
+ }
+
+ private static class WatchHeaderListAdapter extends HeaderViewListAdapter {
+ private View mTopPanel;
+
+ public WatchHeaderListAdapter(
+ ArrayList<ListView.FixedViewInfo> headerViewInfos,
+ ArrayList<ListView.FixedViewInfo> footerViewInfos,
+ ListAdapter adapter) {
+ super(headerViewInfos, footerViewInfos, adapter);
+ }
+
+ public void setTopPanel(View v) {
+ mTopPanel = v;
+ }
+
+ private int getTopPanelCount() {
+ return mTopPanel == null ? 0 : 1;
+ }
+
+ @Override
+ public int getCount() {
+ return super.getCount() + getTopPanelCount();
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return mTopPanel == null && super.areAllItemsEnabled();
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ if (mTopPanel != null) {
+ if (position == 0) {
+ return false;
+ } else {
+ return super.isEnabled(position - 1);
+ }
+ }
+
+ return super.isEnabled(position);
+ }
+
+ @Override
+ public Object getItem(int position) {
+ if (mTopPanel != null) {
+ if (position == 0) {
+ return null;
+ } else {
+ return super.getItem(position - 1);
+ }
+ }
+
+ return super.getItem(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ int numHeaders = getHeadersCount() + getTopPanelCount();
+ if (getWrappedAdapter() != null && position >= numHeaders) {
+ int adjPosition = position - numHeaders;
+ int adapterCount = getWrappedAdapter().getCount();
+ if (adjPosition < adapterCount) {
+ return getWrappedAdapter().getItemId(adjPosition);
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (mTopPanel != null) {
+ if (position == 0) {
+ return mTopPanel;
+ } else {
+ return super.getView(position - 1, convertView, parent);
+ }
+ }
+
+ return super.getView(position, convertView, parent);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ int numHeaders = getHeadersCount() + getTopPanelCount();
+ if (getWrappedAdapter() != null && position >= numHeaders) {
+ int adjPosition = position - numHeaders;
+ int adapterCount = getWrappedAdapter().getCount();
+ if (adjPosition < adapterCount) {
+ return getWrappedAdapter().getItemViewType(adjPosition);
+ }
+ }
+
+ return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/WatchListDecorLayout.java b/core/java/com/android/internal/widget/WatchListDecorLayout.java
new file mode 100644
index 0000000..538ceca
--- /dev/null
+++ b/core/java/com/android/internal/widget/WatchListDecorLayout.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.widget.ListView;
+import android.widget.FrameLayout;
+
+import java.util.ArrayList;
+
+
+/**
+ * Layout for the decor for ListViews on watch-type devices with small screens.
+ * <p>
+ * Supports one panel with the gravity set to top, and one panel with gravity set to bottom.
+ * <p>
+ * Use with one ListView child. The top and bottom panels will track the ListView's scrolling.
+ * If there is no ListView child, it will act like a normal FrameLayout.
+ */
+public class WatchListDecorLayout extends FrameLayout
+ implements ViewTreeObserver.OnScrollChangedListener {
+
+ private int mForegroundPaddingLeft = 0;
+ private int mForegroundPaddingTop = 0;
+ private int mForegroundPaddingRight = 0;
+ private int mForegroundPaddingBottom = 0;
+
+ private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1);
+
+ /** Track the amount the ListView has to scroll up to account for padding change difference. */
+ private int mPendingScroll;
+ private View mBottomPanel;
+ private View mTopPanel;
+ private ListView mListView;
+ private ViewTreeObserver mObserver;
+
+
+ public WatchListDecorLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public WatchListDecorLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public WatchListDecorLayout(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ mPendingScroll = 0;
+
+ for (int i = 0; i < getChildCount(); ++i) {
+ View child = getChildAt(i);
+ if (child instanceof ListView) {
+ if (mListView != null) {
+ throw new IllegalArgumentException("only one ListView child allowed");
+ }
+ mListView = (ListView) child;
+
+ mListView.setNestedScrollingEnabled(true);
+ mObserver = mListView.getViewTreeObserver();
+ mObserver.addOnScrollChangedListener(this);
+ } else {
+ int gravity = (((LayoutParams) child.getLayoutParams()).gravity
+ & Gravity.VERTICAL_GRAVITY_MASK);
+ if (gravity == Gravity.TOP && mTopPanel == null) {
+ mTopPanel = child;
+ } else if (gravity == Gravity.BOTTOM && mBottomPanel == null) {
+ mBottomPanel = child;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ mListView = null;
+ mBottomPanel = null;
+ mTopPanel = null;
+ if (mObserver != null) {
+ if (mObserver.isAlive()) {
+ mObserver.removeOnScrollChangedListener(this);
+ }
+ mObserver = null;
+ }
+ }
+
+ private void applyMeasureToChild(View child, int widthMeasureSpec, int heightMeasureSpec) {
+ final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
+
+ final int childWidthMeasureSpec;
+ if (lp.width == LayoutParams.MATCH_PARENT) {
+ final int width = Math.max(0, getMeasuredWidth()
+ - getPaddingLeftWithForeground() - getPaddingRightWithForeground()
+ - lp.leftMargin - lp.rightMargin);
+ childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ width, MeasureSpec.EXACTLY);
+ } else {
+ childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
+ getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
+ lp.leftMargin + lp.rightMargin,
+ lp.width);
+ }
+
+ final int childHeightMeasureSpec;
+ if (lp.height == LayoutParams.MATCH_PARENT) {
+ final int height = Math.max(0, getMeasuredHeight()
+ - getPaddingTopWithForeground() - getPaddingBottomWithForeground()
+ - lp.topMargin - lp.bottomMargin);
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
+ height, MeasureSpec.EXACTLY);
+ } else {
+ childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
+ getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
+ lp.topMargin + lp.bottomMargin,
+ lp.height);
+ }
+
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ }
+
+ private int measureAndGetHeight(View child, int widthMeasureSpec, int heightMeasureSpec) {
+ if (child != null) {
+ if (child.getVisibility() != GONE) {
+ applyMeasureToChild(mBottomPanel, widthMeasureSpec, heightMeasureSpec);
+ return child.getMeasuredHeight();
+ } else if (getMeasureAllChildren()) {
+ applyMeasureToChild(mBottomPanel, widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int count = getChildCount();
+
+ final boolean measureMatchParentChildren =
+ MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
+ MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
+ mMatchParentChildren.clear();
+
+ int maxHeight = 0;
+ int maxWidth = 0;
+ int childState = 0;
+
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (getMeasureAllChildren() || child.getVisibility() != GONE) {
+ measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ maxWidth = Math.max(maxWidth,
+ child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
+ maxHeight = Math.max(maxHeight,
+ child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
+ childState = combineMeasuredStates(childState, child.getMeasuredState());
+ if (measureMatchParentChildren) {
+ if (lp.width == LayoutParams.MATCH_PARENT ||
+ lp.height == LayoutParams.MATCH_PARENT) {
+ mMatchParentChildren.add(child);
+ }
+ }
+ }
+ }
+
+ // Account for padding too
+ maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
+ maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
+
+ // Check against our minimum height and width
+ maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
+ maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+
+ // Check against our foreground's minimum height and width
+ final Drawable drawable = getForeground();
+ if (drawable != null) {
+ maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
+ maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
+ }
+
+ setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
+ resolveSizeAndState(maxHeight, heightMeasureSpec,
+ childState << MEASURED_HEIGHT_STATE_SHIFT));
+
+ if (mListView != null) {
+ if (mPendingScroll != 0) {
+ mListView.scrollListBy(mPendingScroll);
+ mPendingScroll = 0;
+ }
+
+ int paddingTop = Math.max(mListView.getPaddingTop(),
+ measureAndGetHeight(mTopPanel, widthMeasureSpec, heightMeasureSpec));
+ int paddingBottom = Math.max(mListView.getPaddingBottom(),
+ measureAndGetHeight(mBottomPanel, widthMeasureSpec, heightMeasureSpec));
+
+ if (paddingTop != mListView.getPaddingTop()
+ || paddingBottom != mListView.getPaddingBottom()) {
+ mPendingScroll += mListView.getPaddingTop() - paddingTop;
+ mListView.setPadding(
+ mListView.getPaddingLeft(), paddingTop,
+ mListView.getPaddingRight(), paddingBottom);
+ }
+ }
+
+ count = mMatchParentChildren.size();
+ if (count > 1) {
+ for (int i = 0; i < count; i++) {
+ final View child = mMatchParentChildren.get(i);
+ if (mListView == null || (child != mTopPanel && child != mBottomPanel)) {
+ applyMeasureToChild(child, widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setForegroundGravity(int foregroundGravity) {
+ if (getForegroundGravity() != foregroundGravity) {
+ super.setForegroundGravity(foregroundGravity);
+
+ // calling get* again here because the set above may apply default constraints
+ final Drawable foreground = getForeground();
+ if (getForegroundGravity() == Gravity.FILL && foreground != null) {
+ Rect padding = new Rect();
+ if (foreground.getPadding(padding)) {
+ mForegroundPaddingLeft = padding.left;
+ mForegroundPaddingTop = padding.top;
+ mForegroundPaddingRight = padding.right;
+ mForegroundPaddingBottom = padding.bottom;
+ }
+ } else {
+ mForegroundPaddingLeft = 0;
+ mForegroundPaddingTop = 0;
+ mForegroundPaddingRight = 0;
+ mForegroundPaddingBottom = 0;
+ }
+ }
+ }
+
+ private int getPaddingLeftWithForeground() {
+ return isForegroundInsidePadding() ? Math.max(mPaddingLeft, mForegroundPaddingLeft) :
+ mPaddingLeft + mForegroundPaddingLeft;
+ }
+
+ private int getPaddingRightWithForeground() {
+ return isForegroundInsidePadding() ? Math.max(mPaddingRight, mForegroundPaddingRight) :
+ mPaddingRight + mForegroundPaddingRight;
+ }
+
+ private int getPaddingTopWithForeground() {
+ return isForegroundInsidePadding() ? Math.max(mPaddingTop, mForegroundPaddingTop) :
+ mPaddingTop + mForegroundPaddingTop;
+ }
+
+ private int getPaddingBottomWithForeground() {
+ return isForegroundInsidePadding() ? Math.max(mPaddingBottom, mForegroundPaddingBottom) :
+ mPaddingBottom + mForegroundPaddingBottom;
+ }
+
+ @Override
+ public void onScrollChanged() {
+ if (mListView == null) {
+ return;
+ }
+
+ if (mTopPanel != null) {
+ if (mListView.getChildCount() > 0) {
+ if (mListView.getFirstVisiblePosition() == 0) {
+ View firstChild = mListView.getChildAt(0);
+ setScrolling(mTopPanel,
+ firstChild.getY() - mTopPanel.getHeight() - mTopPanel.getTop());
+ } else {
+ // shift to hide the frame, last child is not the last position
+ setScrolling(mTopPanel, -mTopPanel.getHeight());
+ }
+ } else {
+ setScrolling(mTopPanel, 0); // no visible child, fallback to default behaviour
+ }
+ }
+
+ if (mBottomPanel != null) {
+ if (mListView.getChildCount() > 0) {
+ if (mListView.getLastVisiblePosition() >= mListView.getCount() - 1) {
+ View lastChild = mListView.getChildAt(mListView.getChildCount() - 1);
+ setScrolling(mBottomPanel,
+ lastChild.getY() + lastChild.getHeight() - mBottomPanel.getTop());
+ } else {
+ // shift to hide the frame, last child is not the last position
+ setScrolling(mBottomPanel, mBottomPanel.getHeight());
+ }
+ } else {
+ setScrolling(mBottomPanel, 0); // no visible child, fallback to default behaviour
+ }
+ }
+ }
+
+ /** Only set scrolling for the panel if there is a change in its translationY. */
+ private void setScrolling(View panel, float translationY) {
+ if (panel.getTranslationY() != translationY) {
+ panel.setTranslationY(translationY);
+ }
+ }
+}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 886265a..429131b 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -42,6 +42,8 @@
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
/**
* Loads global system configuration info.
@@ -122,6 +124,11 @@
// These are the permitted backup transport service components
final ArraySet<ComponentName> mBackupTransportWhitelist = new ArraySet<>();
+ // These are the packages of carrier-associated apps which should be disabled until used until
+ // a SIM is inserted which grants carrier privileges to that carrier app.
+ final ArrayMap<String, List<String>> mDisabledUntilUsedPreinstalledCarrierAssociatedApps =
+ new ArrayMap<>();
+
public static SystemConfig getInstance() {
synchronized (SystemConfig.class) {
if (sInstance == null) {
@@ -183,6 +190,10 @@
return mBackupTransportWhitelist;
}
+ public ArrayMap<String, List<String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps() {
+ return mDisabledUntilUsedPreinstalledCarrierAssociatedApps;
+ }
+
SystemConfig() {
// Read configuration from system
readPermissions(Environment.buildPath(
@@ -476,6 +487,26 @@
}
}
XmlUtils.skipCurrentTag(parser);
+ } else if ("disabled-until-used-preinstalled-carrier-associated-app".equals(name)
+ && allowAppConfigs) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ String carrierPkgname = parser.getAttributeValue(null, "carrierAppPackage");
+ if (pkgname == null || carrierPkgname == null) {
+ Slog.w(TAG, "<disabled-until-used-preinstalled-carrier-associated-app"
+ + " without package or carrierAppPackage in " + permFile + " at "
+ + parser.getPositionDescription());
+ } else {
+ List<String> associatedPkgs =
+ mDisabledUntilUsedPreinstalledCarrierAssociatedApps.get(
+ carrierPkgname);
+ if (associatedPkgs == null) {
+ associatedPkgs = new ArrayList<>();
+ mDisabledUntilUsedPreinstalledCarrierAssociatedApps.put(
+ carrierPkgname, associatedPkgs);
+ }
+ associatedPkgs.add(pkgname);
+ }
+ XmlUtils.skipCurrentTag(parser);
} else {
XmlUtils.skipCurrentTag(parser);
continue;
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index d681246..d7550a4 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -18,6 +18,7 @@
#include "CreateJavaOutputStreamAdaptor.h"
#include <Caches.h>
#include <hwui/Paint.h>
+#include <renderthread/RenderProxy.h>
#include "core_jni_helpers.h"
@@ -1361,6 +1362,14 @@
return reinterpret_cast<jlong>(pixelRef);
}
+static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapPtr) {
+ LocalScopedBitmap bitmapHandle(bitmapPtr);
+ if (!bitmapHandle.valid()) return;
+ SkBitmap bitmap;
+ bitmapHandle->getSkBitmap(&bitmap);
+ android::uirenderer::renderthread::RenderProxy::prepareToDraw(bitmap);
+}
+
///////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gBitmapMethods[] = {
@@ -1404,6 +1413,7 @@
(void*)Bitmap_copyPixelsFromBuffer },
{ "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs },
{ "nativeRefPixelRef", "(J)J", (void*)Bitmap_refPixelRef },
+ { "nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw },
};
int register_android_graphics_Bitmap(JNIEnv* env)
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 898cf77..85092ad 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -768,6 +768,21 @@
return false;
}
+ // Don't count glyphs that are the recommended "space" glyph and are zero width.
+ // This logic makes assumptions about HarfBuzz layout, but does correctly handle
+ // cases where ligatures form and zero width space glyphs are left in as
+ // placeholders.
+ static size_t countNonSpaceGlyphs(const Layout& layout) {
+ size_t count = 0;
+ static unsigned int kSpaceGlyphId = 3;
+ for (size_t i = 0; i < layout.nGlyphs(); i++) {
+ if (layout.getGlyphId(i) != kSpaceGlyphId || layout.getCharAdvance(i) != 0.0) {
+ count++;
+ }
+ }
+ return count;
+ }
+
// Returns true if the given string is exact one pair of regional indicators.
static bool isFlag(const jchar* str, size_t length) {
const jchar RI_LEAD_SURROGATE = 0xD83C;
@@ -831,7 +846,7 @@
Layout layout;
MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, str.get(), 0, str.size(),
str.size());
- size_t nGlyphs = layout.nGlyphs();
+ size_t nGlyphs = countNonSpaceGlyphs(layout);
if (nGlyphs != 1 && nChars > 1) {
// multiple-character input, and was not a ligature
// TODO: handle ZWJ/ZWNJ characters specially so we can detect certain ligatures
diff --git a/core/jni/android_app_ApplicationLoaders.cpp b/core/jni/android_app_ApplicationLoaders.cpp
index 24ee959..3e7c039 100644
--- a/core/jni/android_app_ApplicationLoaders.cpp
+++ b/core/jni/android_app_ApplicationLoaders.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "ApplicationLoaders"
+
#include <nativehelper/ScopedUtfChars.h>
#include <nativeloader/native_loader.h>
#include <vulkan/vulkan_loader_data.h>
@@ -22,10 +24,17 @@
static void setupVulkanLayerPath_native(JNIEnv* env, jobject clazz,
jobject classLoader, jstring librarySearchPath) {
+ android_namespace_t* ns = android::FindNamespaceByClassLoader(env, classLoader);
ScopedUtfChars layerPathChars(env, librarySearchPath);
+
vulkan::LoaderData& loader_data = vulkan::LoaderData::GetInstance();
- loader_data.layer_path = layerPathChars.c_str();
- loader_data.app_namespace = android::FindNamespaceByClassLoader(env, classLoader);
+ if (loader_data.layer_path.empty()) {
+ loader_data.layer_path = layerPathChars.c_str();
+ loader_data.app_namespace = ns;
+ } else {
+ ALOGD("ignored Vulkan layer search path %s for namespace %p",
+ layerPathChars.c_str(), ns);
+ }
}
static const JNINativeMethod g_methods[] = {
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index ded4dac..2ab4a35 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -614,10 +614,10 @@
{"native_drawPath","!(JJJ)V", (void*) CanvasJNI::drawPath},
{"nativeDrawVertices", "!(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices},
{"native_drawNinePatch", "!(JJJFFFFJII)V", (void*)CanvasJNI::drawNinePatch},
- {"native_drawBitmap","!(JLandroid/graphics/Bitmap;FFJIII)V", (void*) CanvasJNI::drawBitmap},
+ {"native_drawBitmap","(JLandroid/graphics/Bitmap;FFJIII)V", (void*) CanvasJNI::drawBitmap},
{"nativeDrawBitmapMatrix", "!(JLandroid/graphics/Bitmap;JJ)V", (void*)CanvasJNI::drawBitmapMatrix},
- {"native_drawBitmap","!(JLandroid/graphics/Bitmap;FFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
- {"native_drawBitmap", "!(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
+ {"native_drawBitmap","(JLandroid/graphics/Bitmap;FFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
+ {"native_drawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
{"nativeDrawBitmapMesh", "!(JLandroid/graphics/Bitmap;II[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
{"native_drawText","!(J[CIIFFIJJ)V", (void*) CanvasJNI::drawTextChars},
{"native_drawText","!(JLjava/lang/String;IIFFIJJ)V", (void*) CanvasJNI::drawTextString},
diff --git a/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp b/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp
index 4b2a72d..ade718b 100644
--- a/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp
+++ b/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp
@@ -83,11 +83,13 @@
}
static void addAnimator(JNIEnv*, jobject, jlong animatorSetPtr, jlong propertyHolderPtr,
- jlong interpolatorPtr, jlong startDelay, jlong duration, jint repeatCount) {
+ jlong interpolatorPtr, jlong startDelay, jlong duration, jint repeatCount,
+ jint repeatMode) {
PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr);
PropertyValuesHolder* holder = reinterpret_cast<PropertyValuesHolder*>(propertyHolderPtr);
Interpolator* interpolator = reinterpret_cast<Interpolator*>(interpolatorPtr);
- set->addPropertyAnimator(holder, interpolator, startDelay, duration, repeatCount);
+ RepeatMode mode = static_cast<RepeatMode>(repeatMode);
+ set->addPropertyAnimator(holder, interpolator, startDelay, duration, repeatCount, mode);
}
static jlong createAnimatorSet(JNIEnv*, jobject) {
@@ -95,6 +97,12 @@
return reinterpret_cast<jlong>(animatorSet);
}
+static void setVectorDrawableTarget(JNIEnv*, jobject,jlong animatorPtr, jlong vectorDrawablePtr) {
+ VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(vectorDrawablePtr);
+ PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorPtr);
+ set->setVectorDrawable(tree);
+}
+
static jlong createGroupPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jint propertyId,
jfloat startValue, jfloat endValue) {
VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(nativePtr);
@@ -136,14 +144,24 @@
startValue, endValue);
return reinterpret_cast<jlong>(newHolder);
}
-static void setPropertyHolderData(JNIEnv* env, jobject, jlong propertyHolderPtr,
+static void setFloatPropertyHolderData(JNIEnv* env, jobject, jlong propertyHolderPtr,
jfloatArray srcData, jint length) {
-
jfloat* propertyData = env->GetFloatArrayElements(srcData, nullptr);
- PropertyValuesHolder* holder = reinterpret_cast<PropertyValuesHolder*>(propertyHolderPtr);
+ PropertyValuesHolderImpl<float>* holder =
+ reinterpret_cast<PropertyValuesHolderImpl<float>*>(propertyHolderPtr);
holder->setPropertyDataSource(propertyData, length);
env->ReleaseFloatArrayElements(srcData, propertyData, JNI_ABORT);
}
+
+static void setIntPropertyHolderData(JNIEnv* env, jobject, jlong propertyHolderPtr,
+ jintArray srcData, jint length) {
+ jint* propertyData = env->GetIntArrayElements(srcData, nullptr);
+ PropertyValuesHolderImpl<int>* holder =
+ reinterpret_cast<PropertyValuesHolderImpl<int>*>(propertyHolderPtr);
+ holder->setPropertyDataSource(propertyData, length);
+ env->ReleaseIntArrayElements(srcData, propertyData, JNI_ABORT);
+}
+
static void start(JNIEnv* env, jobject, jlong animatorSetPtr, jobject finishListener, jint id) {
PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr);
AnimationListener* listener = createAnimationListener(env, finishListener, id);
@@ -168,13 +186,15 @@
static const JNINativeMethod gMethods[] = {
{"nCreateAnimatorSet", "()J", (void*)createAnimatorSet},
- {"nAddAnimator", "(JJJJJI)V", (void*)addAnimator},
+ {"nSetVectorDrawableTarget", "(JJ)V", (void*)setVectorDrawableTarget},
+ {"nAddAnimator", "(JJJJJII)V", (void*)addAnimator},
{"nCreateGroupPropertyHolder", "!(JIFF)J", (void*)createGroupPropertyHolder},
{"nCreatePathDataPropertyHolder", "!(JJJ)J", (void*)createPathDataPropertyHolder},
{"nCreatePathColorPropertyHolder", "!(JIII)J", (void*)createPathColorPropertyHolder},
{"nCreatePathPropertyHolder", "!(JIFF)J", (void*)createPathPropertyHolder},
{"nCreateRootAlphaPropertyHolder", "!(JFF)J", (void*)createRootAlphaPropertyHolder},
- {"nSetPropertyHolderData", "(J[FI)V", (void*)setPropertyHolderData},
+ {"nSetPropertyHolderData", "(J[FI)V", (void*)setFloatPropertyHolderData},
+ {"nSetPropertyHolderData", "(J[II)V", (void*)setIntPropertyHolderData},
{"nStart", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V", (void*)start},
{"nReverse", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V", (void*)reverse},
{"nEnd", "!(J)V", (void*)end},
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index f37fd78..48f6b49 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -787,109 +787,16 @@
// exception thrown by ScopedUtfChars
return 0;
}
- size_t keyLength = strlen(key);
-
ALOGV("%s (key = '%s')", __FUNCTION__, key);
- sp<VendorTagDescriptor> vTags = VendorTagDescriptor::getGlobalVendorTagDescriptor();
-
- SortedVector<String8> vendorSections;
- size_t vendorSectionCount = 0;
-
- if (vTags != NULL) {
- vendorSections = vTags->getAllSectionNames();
- vendorSectionCount = vendorSections.size();
- }
-
- // First, find the section by the longest string match
- const char *section = NULL;
- size_t sectionIndex = 0;
- size_t sectionLength = 0;
- size_t totalSectionCount = ANDROID_SECTION_COUNT + vendorSectionCount;
- for (size_t i = 0; i < totalSectionCount; ++i) {
-
- const char *str = (i < ANDROID_SECTION_COUNT) ? camera_metadata_section_names[i] :
- vendorSections[i - ANDROID_SECTION_COUNT].string();
- if (kIsDebug) {
- ALOGV("%s: Trying to match against section '%s'", __FUNCTION__, str);
- }
- if (strstr(key, str) == key) { // key begins with the section name
- size_t strLength = strlen(str);
-
- if (kIsDebug) {
- ALOGV("%s: Key begins with section name", __FUNCTION__);
- }
-
- // section name is the longest we've found so far
- if (section == NULL || sectionLength < strLength) {
- section = str;
- sectionIndex = i;
- sectionLength = strLength;
-
- if (kIsDebug) {
- ALOGV("%s: Found new best section (%s)", __FUNCTION__, section);
- }
- }
- }
- }
-
- // TODO: Make above get_camera_metadata_section_from_name ?
-
- if (section == NULL) {
- jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
- "Could not find section name for key '%s')", key);
- return 0;
- } else {
- ALOGV("%s: Found matched section '%s' (%zu)",
- __FUNCTION__, section, sectionIndex);
- }
-
- // Get the tag name component of the key
- const char *keyTagName = key + sectionLength + 1; // x.y.z -> z
- if (sectionLength + 1 >= keyLength) {
- jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
- "Key length too short for key '%s')", key);
- return 0;
- }
-
- // Match rest of name against the tag names in that section only
uint32_t tag = 0;
- if (sectionIndex < ANDROID_SECTION_COUNT) {
- // Match built-in tags (typically android.*)
- uint32_t tagBegin, tagEnd; // [tagBegin, tagEnd)
- tagBegin = camera_metadata_section_bounds[sectionIndex][0];
- tagEnd = camera_metadata_section_bounds[sectionIndex][1];
-
- for (tag = tagBegin; tag < tagEnd; ++tag) {
- const char *tagName = get_camera_metadata_tag_name(tag);
-
- if (strcmp(keyTagName, tagName) == 0) {
- ALOGV("%s: Found matched tag '%s' (%d)",
- __FUNCTION__, tagName, tag);
- break;
- }
- }
-
- if (tag == tagEnd) {
- jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
- "Could not find tag name for key '%s')", key);
- return 0;
- }
- } else if (vTags != NULL) {
- // Match vendor tags (typically com.*)
- const String8 sectionName(section);
- const String8 tagName(keyTagName);
-
- status_t res = OK;
- if ((res = vTags->lookupTag(tagName, sectionName, &tag)) != OK) {
- jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
- "%s: No vendor tag matches key '%s'", __FUNCTION__, key);
- return 0;
- }
+ sp<VendorTagDescriptor> vTags =
+ VendorTagDescriptor::getGlobalVendorTagDescriptor();
+ status_t res = CameraMetadata::getTagFromName(key, vTags.get(), &tag);
+ if (res != OK) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "Could not find tag for key '%s')", key);
}
-
- // TODO: Make above get_camera_metadata_tag_from_name ?
-
return tag;
}
diff --git a/core/jni/android_hardware_location_ContextHubService.cpp b/core/jni/android_hardware_location_ContextHubService.cpp
index 3881d6d..9515a0e 100644
--- a/core/jni/android_hardware_location_ContextHubService.cpp
+++ b/core/jni/android_hardware_location_ContextHubService.cpp
@@ -21,28 +21,34 @@
#include <inttypes.h>
#include <jni.h>
-#include <queue>
-#include <unordered_map>
+#include <mutex>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <unordered_map>
+#include <queue>
#include <cutils/log.h>
#include "JNIHelp.h"
#include "core_jni_helpers.h"
-static constexpr int OS_APP_ID=-1;
+static constexpr int OS_APP_ID = -1;
+static constexpr uint64_t ALL_APPS = UINT64_C(0xFFFFFFFFFFFFFFFF);
-static constexpr int MIN_APP_ID=1;
-static constexpr int MAX_APP_ID=128;
+static constexpr int MIN_APP_ID = 1;
+static constexpr int MAX_APP_ID = 128;
-static constexpr size_t MSG_HEADER_SIZE=4;
-static constexpr int HEADER_FIELD_MSG_TYPE=0;
-//static constexpr int HEADER_FIELD_MSG_VERSION=1;
-static constexpr int HEADER_FIELD_HUB_HANDLE=2;
-static constexpr int HEADER_FIELD_APP_INSTANCE=3;
+static constexpr size_t MSG_HEADER_SIZE = 4;
+static constexpr size_t HEADER_FIELD_MSG_TYPE = 0;
+static constexpr size_t HEADER_FIELD_MSG_VERSION = 1;
+static constexpr size_t HEADER_FIELD_HUB_HANDLE = 2;
+static constexpr size_t HEADER_FIELD_APP_INSTANCE = 3;
+
+static constexpr size_t HEADER_FIELD_LOAD_APP_ID_LO = MSG_HEADER_SIZE;
+static constexpr size_t HEADER_FIELD_LOAD_APP_ID_HI = MSG_HEADER_SIZE + 1;
+static constexpr size_t MSG_HEADER_SIZE_LOAD_APP = MSG_HEADER_SIZE + 2;
namespace android {
@@ -83,6 +89,7 @@
jmethodID contextHubServiceMsgReceiptCallback;
jmethodID contextHubServiceAddAppInstance;
+ jmethodID contextHubServiceDeleteAppInstance;
};
struct context_hub_info_s {
@@ -93,10 +100,53 @@
};
struct app_instance_info_s {
- uint32_t hubHandle; // Id of the hub this app is on
- int instanceId; // systemwide unique instance id - assigned
+ uint64_t truncName; // Possibly truncated name for logging
+ uint32_t hubHandle; // Id of the hub this app is on
+ int instanceId; // system wide unique instance id - assigned
struct hub_app_info appInfo; // returned from the HAL
- uint64_t truncName; // Possibly truncated name - logging
+};
+
+/*
+ * TODO(ashutoshj): From original code review:
+ *
+ * So, I feel like we could possible do a better job of organizing this code,
+ * and being more C++-y. Consider something like this:
+ * class TxnManager {
+ * public:
+ * TxnManager();
+ * ~TxnManager();
+ * int add(hub_message_e identifier, void *data);
+ * int close();
+ * bool isPending() const;
+ * int fetchData(hub_message_e *identifier, void **data) const;
+ *
+ * private:
+ * bool mPending;
+ * mutable std::mutex mLock;
+ * hub_message_e mIdentifier;
+ * void *mData;
+ * };
+ *
+ * And then, for example, we'd have things like:
+ * TxnManager::TxnManager() : mPending(false), mLock(), mIdentifier(), mData(nullptr) {}
+ * int TxnManager::add(hub_message_e identifier, void *data) {
+ * std::lock_guard<std::mutex> lock(mLock);
+ * mPending = true;
+ * mData = txnData;
+ * mIdentifier = txnIdentifier;
+ * return 0;
+ * }
+ * And then calling code would look like:
+ * if (!db.txnManager.add(CONTEXT_HUB_LOAD_APP, txnInfo)) {
+ *
+ * This would make it clearer the nothing is manipulating any state within TxnManager
+ * unsafely and outside of these couple of calls.
+ */
+struct txnManager_s {
+ bool txnPending; // Is a transaction pending
+ std::mutex m; // mutex for manager
+ hub_messages_e txnIdentifier; // What are we doing
+ void *txnData; // Details
};
struct contextHubServiceDb_s {
@@ -105,12 +155,69 @@
jniInfo_s jniInfo;
std::queue<int> freeIds;
std::unordered_map<int, app_instance_info_s> appInstances;
+ txnManager_s txnManager;
};
} // unnamed namespace
static contextHubServiceDb_s db;
+static bool initTxnManager() {
+ txnManager_s *mgr = &db.txnManager;
+
+ mgr->txnData = nullptr;
+ mgr->txnPending = false;
+ return true;
+}
+
+static int addTxn(hub_messages_e txnIdentifier, void *txnData) {
+ txnManager_s *mgr = &db.txnManager;
+
+ std::lock_guard<std::mutex>lock(mgr->m);
+
+ mgr->txnPending = true;
+ mgr->txnData = txnData;
+ mgr->txnIdentifier = txnIdentifier;
+
+ return 0;
+}
+
+static int closeTxn() {
+ txnManager_s *mgr = &db.txnManager;
+ std::lock_guard<std::mutex>lock(mgr->m);
+ mgr->txnPending = false;
+ free(mgr->txnData);
+ mgr->txnData = nullptr;
+
+ return 0;
+}
+
+static bool isTxnPending() {
+ txnManager_s *mgr = &db.txnManager;
+ std::lock_guard<std::mutex>lock(mgr->m);
+ return mgr->txnPending;
+}
+
+static int fetchTxnData(hub_messages_e *id, void **data) {
+ txnManager_s *mgr = &db.txnManager;
+
+ if (!id || !data) {
+ ALOGW("Null params id %p, data %p", id, data);
+ return -1;
+ }
+
+ std::lock_guard<std::mutex>lock(mgr->m);
+ if (!mgr->txnPending) {
+ ALOGW("No Transactions pending");
+ return -1;
+ }
+
+ // else
+ *id = mgr->txnIdentifier;
+ *data = mgr->txnData;
+ return 0;
+}
+
int context_hub_callback(uint32_t hubId, const struct hub_message_t *msg,
void *cookie);
@@ -152,13 +259,21 @@
}
}
-static int get_hub_id_for_app_instance(int id) {
+static int get_hub_handle_for_app_instance(int id) {
if (!db.appInstances.count(id)) {
ALOGD("%s: Cannot find app for app instance %d", __FUNCTION__, id);
return -1;
}
- int hubHandle = db.appInstances[id].hubHandle;
+ return db.appInstances[id].hubHandle;
+}
+
+static int get_hub_id_for_app_instance(int id) {
+ int hubHandle = get_hub_handle_for_app_instance(id);
+
+ if (hubHandle < 0) {
+ return -1;
+ }
return db.hubInfo.hubs[hubHandle].hub_id;
}
@@ -184,29 +299,41 @@
return 0;
}
-static void send_query_for_apps() {
+static void query_hub_for_apps(uint64_t appId, uint32_t hubHandle) {
hub_message_t msg;
+ query_apps_request_t queryMsg;
+
+ queryMsg.app_name.id = NANOAPP_VENDOR_ALL_APPS;
msg.message_type = CONTEXT_HUB_QUERY_APPS;
- msg.message_len = 0;
+ msg.message_len = sizeof(queryMsg);
+ msg.message = &queryMsg;
+ ALOGD("Sending query for apps to hub %" PRIu32, hubHandle);
+ set_os_app_as_destination(&msg, hubHandle);
+ if (send_msg_to_hub(&msg, hubHandle) != 0) {
+ ALOGW("Could not query hub %" PRIu32 " for apps", hubHandle);
+ }
+}
+
+static void sendQueryForApps(uint64_t appId) {
for (int i = 0; i < db.hubInfo.numHubs; i++ ) {
- ALOGD("Sending query for apps to hub %d", i);
- set_os_app_as_destination(&msg, i);
- if (send_msg_to_hub(&msg, i) != 0) {
- ALOGW("Could not query hub %i for apps", i);
- }
+ query_hub_for_apps(appId, i);
}
}
static int return_id(int id) {
// Note : This method is not thread safe.
- // id returned is guarenteed to be in use
- db.freeIds.push(id);
- return 0;
+ // id returned is guaranteed to be in use
+ if (id >= 0) {
+ db.freeIds.push(id);
+ return 0;
+ }
+
+ return -1;
}
-static int generate_id(void) {
+static int generate_id() {
// Note : This method is not thread safe.
int retVal = -1;
@@ -218,23 +345,31 @@
return retVal;
}
-int add_app_instance(const hub_app_info *appInfo, uint32_t hubHandle, JNIEnv *env) {
+
+static int add_app_instance(const hub_app_info *appInfo, uint32_t hubHandle,
+ int appInstanceHandle, JNIEnv *env) {
+
+ ALOGI("Loading App");
+
// Not checking if the apps are indeed distinct
app_instance_info_s entry;
- int appInstanceHandle = generate_id();
-
assert(appInfo);
- if (appInstanceHandle < 0) {
- ALOGE("Cannot find resources to add app instance %d",
- appInstanceHandle);
- return -1;
+ if (db.appInstances.count(appInstanceHandle) == 0) {
+ appInstanceHandle = generate_id();
+ if (appInstanceHandle < 0) {
+ ALOGE("Cannot find resources to add app instance %d",
+ appInstanceHandle);
+ return -1;
+ }
}
entry.appInfo = *appInfo;
+
entry.instanceId = appInstanceHandle;
entry.truncName = appInfo->app_name.id;
entry.hubHandle = hubHandle;
+
db.appInstances[appInstanceHandle] = entry;
// Finally - let the service know of this app instance
@@ -250,17 +385,70 @@
return appInstanceHandle;
}
-int delete_app_instance(int id) {
+int delete_app_instance(int id, JNIEnv *env) {
if (!db.appInstances.count(id)) {
+ ALOGW("Cannot find App id : %d", id);
return -1;
}
return_id(id);
db.appInstances.erase(id);
+ if (env->CallIntMethod(db.jniInfo.jContextHubService,
+ db.jniInfo.contextHubServiceDeleteAppInstance,
+ id) != 0) {
+ ALOGW("Could not delete App id : %d", id);
+ return -1;
+ }
+
+ ALOGI("Deleted App id : %d", id);
return 0;
}
+static int startLoadAppTxn(uint64_t appId, int hubHandle) {
+ app_instance_info_s *txnInfo = (app_instance_info_s *)malloc(sizeof(app_instance_info_s));
+ int instanceId = generate_id();
+
+ if (!txnInfo || instanceId < 0) {
+ return_id(instanceId);
+ free(txnInfo);
+ return -1;
+ }
+
+ txnInfo->truncName = appId;
+ txnInfo->hubHandle = hubHandle;
+ txnInfo->instanceId = instanceId;
+
+ txnInfo->appInfo.app_name.id = appId;
+ txnInfo->appInfo.num_mem_ranges = 0;
+ txnInfo->appInfo.version = -1; // Awaited
+
+ if (addTxn(CONTEXT_HUB_LOAD_APP, txnInfo) != 0) {
+ return_id(instanceId);
+ free(txnInfo);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int startUnloadAppTxn(uint32_t appInstanceHandle) {
+ uint32_t *txnData = (uint32_t *) malloc(sizeof(uint32_t));
+ if (!txnData) {
+ ALOGW("Cannot allocate memory to start unload transaction");
+ return -1;
+ }
+
+ *txnData = appInstanceHandle;
+
+ if (addTxn(CONTEXT_HUB_UNLOAD_APP, txnData) != 0) {
+ free(txnData);
+ ALOGW("Cannot start transaction to unload app");
+ return -1;
+ }
+
+ return 0;
+}
static void initContextHubService() {
int err = 0;
@@ -281,6 +469,7 @@
db.freeIds.push(i);
}
+ initTxnManager();
if (db.hubInfo.contextHubModule) {
int retNumHubs = db.hubInfo.contextHubModule->get_hubs(db.hubInfo.contextHubModule,
&db.hubInfo.hubs);
@@ -298,6 +487,7 @@
for (i = 0; i < db.hubInfo.numHubs; i++) {
db.hubInfo.cookies[i] = db.hubInfo.hubs[i].hub_id;
+ ALOGI("Subscribing to hubHandle %d with OS App name %" PRIu64, i, db.hubInfo.hubs[i].os_app_name.id);
if (db.hubInfo.contextHubModule->subscribe_messages(db.hubInfo.hubs[i].hub_id,
context_hub_callback,
&db.hubInfo.cookies[i]) == 0) {
@@ -305,7 +495,7 @@
}
}
- send_query_for_apps();
+ sendQueryForApps(ALL_APPS);
} else {
ALOGW("No Context Hub Module present");
}
@@ -342,7 +532,8 @@
return ret;
}
-int handle_query_apps_response(char *msg, int msgLen, uint32_t hubHandle) {
+int handle_query_apps_response(const uint8_t *msg, int msgLen,
+ uint32_t hubHandle) {
JNIEnv *env;
if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
return -1;
@@ -350,53 +541,202 @@
int numApps = msgLen/sizeof(hub_app_info);
hub_app_info info;
- hub_app_info *unalignedInfoAddr = (hub_app_info*)msg;
+ const hub_app_info *unalignedInfoAddr = (const hub_app_info*)msg;
for (int i = 0; i < numApps; i++, unalignedInfoAddr++) {
memcpy(&info, unalignedInfoAddr, sizeof(info));
- add_app_instance(&info, hubHandle, env);
+ // We will only have one instance of the app
+ // TODO : Change this logic once we support multiple instances of the same app
+ int appInstance = get_app_instance_for_app_id(info.app_name.id);
+ add_app_instance(&info, hubHandle, appInstance, env);
}
return 0;
}
+static void passOnOsResponse(uint32_t hubHandle, uint32_t msgType,
+ status_response_t *rsp, int8_t *additionalData,
+ size_t additionalDataLen) {
+ JNIEnv *env;
-int handle_os_message(uint32_t msgType, uint32_t hubHandle,
- char *msg, int msgLen) {
- int retVal;
+ if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
+ ALOGW("Cannot latch to JNI env, dropping OS response %" PRIu32, msgType);
+ return;
+ }
- //ALOGD("Rcd OS message from hubHandle %" PRIu32 " type %" PRIu32 " length %d",
- // hubHandle, msgType, msgLen);
+ uint32_t header[MSG_HEADER_SIZE];
+ memset(header, 0, sizeof(header));
+
+ if (!additionalData) {
+ additionalDataLen = 0; // clamp
+ }
+ int msgLen = 1 + additionalDataLen;
+
+ int8_t *msg = new int8_t[msgLen];
+
+ if (!msg) {
+ ALOGW("Unexpected : Ran out of memory, cannot send response");
+ return;
+ }
+
+ header[HEADER_FIELD_MSG_TYPE] = msgType;
+ header[HEADER_FIELD_MSG_VERSION] = 0;
+ header[HEADER_FIELD_HUB_HANDLE] = hubHandle;
+ header[HEADER_FIELD_APP_INSTANCE] = OS_APP_ID;
+
+ msg[0] = rsp->result;
+
+ if (additionalData) {
+ memcpy(&msg[1], additionalData, additionalDataLen);
+ }
+
+ jbyteArray jmsg = env->NewByteArray(msgLen);
+ jintArray jheader = env->NewIntArray(sizeof(header));
+
+ env->SetByteArrayRegion(jmsg, 0, msgLen, (jbyte *)msg);
+ env->SetIntArrayRegion(jheader, 0, sizeof(header), (jint *)header);
+
+ ALOGI("Passing msg type %" PRIu32 " from app %" PRIu32 " from hub %" PRIu32,
+ header[HEADER_FIELD_MSG_TYPE], header[HEADER_FIELD_APP_INSTANCE],
+ header[HEADER_FIELD_HUB_HANDLE]);
+
+ env->CallIntMethod(db.jniInfo.jContextHubService,
+ db.jniInfo.contextHubServiceMsgReceiptCallback,
+ jheader, jmsg);
+
+ delete[] msg;
+}
+
+void closeUnloadTxn(bool success) {
+ void *txnData = nullptr;
+ hub_messages_e txnId;
+
+ if (success && fetchTxnData(&txnId, &txnData) == 0 &&
+ txnId == CONTEXT_HUB_UNLOAD_APP) {
+ db.appInstances.erase(*(uint32_t *)txnData);
+ } else {
+ ALOGW("Could not unload the app successfully ! success %d, txnData %p", success, txnData);
+ }
+
+ closeTxn();
+}
+
+void closeLoadTxn(bool success, int *appInstanceHandle) {
+ void *txnData;
+ hub_messages_e txnId;
+
+ if (success && fetchTxnData(&txnId, &txnData) == 0 &&
+ txnId == CONTEXT_HUB_LOAD_APP) {
+ app_instance_info_s *info = (app_instance_info_s *)txnData;
+ *appInstanceHandle = info->instanceId;
+
+ JNIEnv *env;
+ if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) == JNI_OK) {
+ add_app_instance(&info->appInfo, info->hubHandle, info->instanceId, env);
+ } else {
+ ALOGW("Could not attach to JVM !");
+ }
+ sendQueryForApps(info->appInfo.app_name.id);
+ } else {
+ ALOGW("Could not load the app successfully ! Unexpected failure");
+ }
+
+ closeTxn();
+}
+
+static bool isValidOsStatus(const uint8_t *msg, size_t msgLen,
+ status_response_t *rsp) {
+ // Workaround a bug in some HALs
+ if (msgLen == 1) {
+ rsp->result = msg[0];
+ return true;
+ }
+
+ if (!msg || msgLen != sizeof(*rsp)) {
+ ALOGW("Received invalid response %p of size %zu", msg, msgLen);
+ return false;
+ }
+
+ memcpy(rsp, msg, sizeof(*rsp));
+
+ // No sanity checks on return values
+ return true;
+}
+
+static void invalidateNanoApps(uint32_t hubHandle) {
+ JNIEnv *env;
+
+ if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
+ ALOGW("Could not attach to JVM !");
+ }
+
+ auto end = db.appInstances.end();
+ for (auto current = db.appInstances.begin(); current != end; ) {
+ app_instance_info_s info = current->second;
+ current++;
+ if (info.hubHandle == hubHandle) {
+ delete_app_instance(info.instanceId, env);
+ }
+ }
+}
+
+static int handle_os_message(uint32_t msgType, uint32_t hubHandle,
+ const uint8_t *msg, int msgLen) {
+ int retVal = -1;
+
+ ALOGD("Rcd OS message from hubHandle %" PRIu32 " type %" PRIu32 " length %d",
+ hubHandle, msgType, msgLen);
+
+ struct status_response_t rsp;
switch(msgType) {
- case CONTEXT_HUB_APPS_ENABLE:
- retVal = 0;
- break;
- case CONTEXT_HUB_APPS_DISABLE:
- retVal = 0;
- break;
+ case CONTEXT_HUB_APPS_ENABLE:
+ case CONTEXT_HUB_APPS_DISABLE:
+ case CONTEXT_HUB_LOAD_APP:
+ case CONTEXT_HUB_UNLOAD_APP:
+ if (isValidOsStatus(msg, msgLen, &rsp)) {
+ if (msgType == CONTEXT_HUB_LOAD_APP) {
+ int appInstanceHandle;
+ closeLoadTxn(rsp.result == 0, &appInstanceHandle);
+ passOnOsResponse(hubHandle, msgType, &rsp, (int8_t *)(&appInstanceHandle),
+ sizeof(appInstanceHandle));
+ } else if (msgType == CONTEXT_HUB_UNLOAD_APP) {
+ closeUnloadTxn(rsp.result == 0);
+ passOnOsResponse(hubHandle, msgType, &rsp, nullptr, 0);
+ } else {
+ passOnOsResponse(hubHandle, msgType, &rsp, nullptr, 0);
+ }
+ retVal = 0;
+ }
+ break;
- case CONTEXT_HUB_LOAD_APP:
- retVal = 0;
- break;
+ case CONTEXT_HUB_QUERY_APPS:
+ rsp.result = 0;
+ retVal = handle_query_apps_response(msg, msgLen, hubHandle);
+ passOnOsResponse(hubHandle, msgType, &rsp, nullptr, 0);
+ break;
- case CONTEXT_HUB_UNLOAD_APP:
- retVal = 0;
- break;
+ case CONTEXT_HUB_QUERY_MEMORY:
+ // Deferring this use
+ retVal = 0;
+ break;
- case CONTEXT_HUB_QUERY_APPS:
- retVal = handle_query_apps_response(msg, msgLen, hubHandle);
- break;
+ case CONTEXT_HUB_OS_REBOOT:
+ if (isValidOsStatus(msg, msgLen, &rsp)) {
+ rsp.result = 0;
+ ALOGW("Context Hub handle %d restarted", hubHandle);
+ closeTxn();
+ passOnOsResponse(hubHandle, msgType, &rsp, nullptr, 0);
+ invalidateNanoApps(hubHandle);
+ query_hub_for_apps(ALL_APPS, hubHandle);
+ retVal = 0;
+ }
+ break;
- case CONTEXT_HUB_QUERY_MEMORY:
- retVal = 0;
- break;
-
- default:
- retVal = -1;
- break;
-
+ default:
+ retVal = -1;
+ break;
}
return retVal;
@@ -416,10 +756,12 @@
}
}
+
int context_hub_callback(uint32_t hubId,
const struct hub_message_t *msg,
void *cookie) {
if (!msg) {
+ ALOGW("NULL message");
return -1;
}
if (!sanity_check_cookie(cookie, hubId)) {
@@ -429,11 +771,12 @@
return -1;
}
+
uint32_t messageType = msg->message_type;
uint32_t hubHandle = *(uint32_t*) cookie;
if (messageType < CONTEXT_HUB_TYPE_PRIVATE_MSG_BASE) {
- handle_os_message(messageType, hubHandle, (char*) msg->message, msg->message_len);
+ handle_os_message(messageType, hubHandle, (uint8_t*) msg->message, msg->message_len);
} else {
int appHandle = get_app_instance_for_app_id(msg->app_name.id);
if (appHandle < 0) {
@@ -524,7 +867,9 @@
env->GetMethodID(db.jniInfo.contextHubServiceClass,
"addAppInstance", "(IIJI)I");
-
+ db.jniInfo.contextHubServiceDeleteAppInstance =
+ env->GetMethodID(db.jniInfo.contextHubServiceClass,
+ "deleteAppInstance", "(I)I");
return 0;
}
@@ -534,8 +879,6 @@
jintArray jintBuf;
jobjectArray jmemBuf;
- int dummyConnectedSensors[] = {1, 2, 3, 4, 5};
-
jobject jHub = env->NewObject(db.jniInfo.contextHubInfoClass,
db.jniInfo.contextHubInfoCtor);
env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetId, hub->hub_id);
@@ -565,11 +908,21 @@
hub->max_supported_msg_len);
- // TODO : jintBuf = env->NewIntArray(hub->num_connected_sensors);
- // TODO : env->SetIntArrayRegion(jintBuf, 0, hub->num_connected_sensors,
- // hub->connected_sensors);
- jintBuf = env->NewIntArray(array_length(dummyConnectedSensors));
- env->SetIntArrayRegion(jintBuf, 0, hub->num_connected_sensors, dummyConnectedSensors);
+ jintBuf = env->NewIntArray(hub->num_connected_sensors);
+ int *connectedSensors = new int[hub->num_connected_sensors];
+
+ if (!connectedSensors) {
+ ALOGW("Cannot allocate memory! Unexpected");
+ assert(false);
+ } else {
+ for (unsigned int i = 0; i < hub->num_connected_sensors; i++) {
+ connectedSensors[i] = hub->connected_sensors[i].sensor_id;
+ }
+ }
+
+ env->SetIntArrayRegion(jintBuf, 0, hub->num_connected_sensors,
+ connectedSensors);
+
env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSupportedSensors, jintBuf);
env->DeleteLocalRef(jintBuf);
@@ -580,6 +933,7 @@
env->DeleteLocalRef(jmemBuf);
+ delete[] connectedSensors;
return jHub;
}
@@ -618,33 +972,98 @@
jbyte *data = env->GetByteArrayElements(data_, 0);
int dataBufferLength = env->GetArrayLength(data_);
+ if (numHeaderElements < MSG_HEADER_SIZE) {
+ ALOGW("Malformed header len");
+ return -1;
+ }
- if (numHeaderElements >= MSG_HEADER_SIZE) {
- bool setAddressSuccess;
- int hubId;
- hub_message_t msg;
+ uint32_t appInstanceHandle = header[HEADER_FIELD_APP_INSTANCE];
+ uint32_t msgType = header[HEADER_FIELD_MSG_TYPE];
+ int hubHandle = -1;
+ int hubId;
+ uint64_t appId;
+
+ if (msgType == CONTEXT_HUB_UNLOAD_APP) {
+ hubHandle = get_hub_handle_for_app_instance(appInstanceHandle);
+ } else if (msgType == CONTEXT_HUB_LOAD_APP) {
+ if (numHeaderElements < MSG_HEADER_SIZE_LOAD_APP) {
+ return -1;
+ }
+ uint64_t appIdLo = header[HEADER_FIELD_LOAD_APP_ID_LO];
+ uint64_t appIdHi = header[HEADER_FIELD_LOAD_APP_ID_HI];
+ appId = appIdHi << 32 | appIdLo;
+
+ hubHandle = header[HEADER_FIELD_HUB_HANDLE];
+ } else {
+ hubHandle = header[HEADER_FIELD_HUB_HANDLE];
+ }
+
+ if (hubHandle < 0) {
+ ALOGD("Invalid hub Handle %d", hubHandle);
+ return -1;
+ }
+
+ if (msgType == CONTEXT_HUB_LOAD_APP ||
+ msgType == CONTEXT_HUB_UNLOAD_APP) {
+
+ if (isTxnPending()) {
+ ALOGW("Cannot load or unload app while a transaction is pending !");
+ return -1;
+ }
+
+ if (msgType == CONTEXT_HUB_LOAD_APP) {
+ if (startLoadAppTxn(appId, hubHandle) != 0) {
+ ALOGW("Cannot Start Load Transaction");
+ return -1;
+ }
+ } else if (msgType == CONTEXT_HUB_UNLOAD_APP) {
+ if (startUnloadAppTxn(appInstanceHandle) != 0) {
+ ALOGW("Cannot Start UnLoad Transaction");
+ return -1;
+ }
+ }
+ }
+
+ bool setAddressSuccess = false;
+ hub_message_t msg;
+
+ msg.message_type = msgType;
+
+ if (msgType == CONTEXT_HUB_UNLOAD_APP) {
+ msg.message_len = sizeof(db.appInstances[appInstanceHandle].appInfo.app_name);
+ msg.message = &db.appInstances[appInstanceHandle].appInfo.app_name;
+ setAddressSuccess = (set_os_app_as_destination(&msg, hubHandle) == 0);
+ hubId = get_hub_id_for_hub_handle(hubHandle);
+ } else {
+ msg.message_len = dataBufferLength;
+ msg.message = data;
if (header[HEADER_FIELD_APP_INSTANCE] == OS_APP_ID) {
- setAddressSuccess = (set_os_app_as_destination(&msg, header[HEADER_FIELD_HUB_HANDLE]) == 0);
- hubId = get_hub_id_for_hub_handle(header[HEADER_FIELD_HUB_HANDLE]);
+ setAddressSuccess = (set_os_app_as_destination(&msg, hubHandle) == 0);
+ hubId = get_hub_id_for_hub_handle(hubHandle);
} else {
setAddressSuccess = (set_dest_app(&msg, header[HEADER_FIELD_APP_INSTANCE]) == 0);
hubId = get_hub_id_for_app_instance(header[HEADER_FIELD_APP_INSTANCE]);
}
+ }
- if (setAddressSuccess && hubId >= 0) {
- msg.message_type = header[HEADER_FIELD_MSG_TYPE];
- msg.message_len = dataBufferLength;
- msg.message = data;
- retVal = db.hubInfo.contextHubModule->send_message(hubId, &msg);
- } else {
- ALOGD("Could not find app instance %d on hubHandle %d, setAddress %d",
- header[HEADER_FIELD_APP_INSTANCE],
- header[HEADER_FIELD_HUB_HANDLE],
- (int)setAddressSuccess);
- }
+ if (setAddressSuccess && hubId >= 0) {
+ ALOGD("Asking HAL to remove app");
+ retVal = db.hubInfo.contextHubModule->send_message(hubId, &msg);
} else {
- ALOGD("Malformed header len");
+ ALOGD("Could not find app instance %d on hubHandle %d, setAddress %d",
+ header[HEADER_FIELD_APP_INSTANCE],
+ header[HEADER_FIELD_HUB_HANDLE],
+ (int)setAddressSuccess);
+ }
+
+ if (retVal != 0) {
+ ALOGD("Send Message failure - %d", retVal);
+ if (msgType == CONTEXT_HUB_LOAD_APP) {
+ closeLoadTxn(false, nullptr);
+ } else if (msgType == CONTEXT_HUB_UNLOAD_APP) {
+ closeUnloadTxn(false);
+ }
}
env->ReleaseIntArrayElements(header_, header, 0);
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index 6513304..092aaf6 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -20,18 +20,20 @@
#include <system/audio.h>
// keep these values in sync with AudioFormat.java
-#define ENCODING_PCM_16BIT 2
-#define ENCODING_PCM_8BIT 3
-#define ENCODING_PCM_FLOAT 4
-#define ENCODING_AC3 5
-#define ENCODING_E_AC3 6
-#define ENCODING_DTS 7
-#define ENCODING_DTS_HD 8
-#define ENCODING_MP3 9
-#define ENCODING_AAC_LC 10
-#define ENCODING_AAC_HE_V1 11
-#define ENCODING_AAC_HE_V2 12
-#define ENCODING_IEC61937 13
+#define ENCODING_PCM_16BIT 2
+#define ENCODING_PCM_8BIT 3
+#define ENCODING_PCM_FLOAT 4
+#define ENCODING_AC3 5
+#define ENCODING_E_AC3 6
+#define ENCODING_DTS 7
+#define ENCODING_DTS_HD 8
+#define ENCODING_MP3 9
+#define ENCODING_AAC_LC 10
+#define ENCODING_AAC_HE_V1 11
+#define ENCODING_AAC_HE_V2 12
+#define ENCODING_IEC61937 13
+#define ENCODING_DOLBY_TRUEHD 14
+
#define ENCODING_INVALID 0
#define ENCODING_DEFAULT 1
@@ -65,6 +67,8 @@
return AUDIO_FORMAT_AAC_HE_V1;
case ENCODING_AAC_HE_V2:
return AUDIO_FORMAT_AAC_HE_V2;
+ case ENCODING_DOLBY_TRUEHD:
+ return AUDIO_FORMAT_DOLBY_TRUEHD;
case ENCODING_IEC61937:
return AUDIO_FORMAT_IEC61937;
case ENCODING_DEFAULT:
@@ -108,6 +112,8 @@
return ENCODING_AAC_HE_V2;
case AUDIO_FORMAT_IEC61937:
return ENCODING_IEC61937;
+ case AUDIO_FORMAT_DOLBY_TRUEHD:
+ return ENCODING_DOLBY_TRUEHD;
case AUDIO_FORMAT_DEFAULT:
return ENCODING_DEFAULT;
default:
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index ef16ef5..49760021 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -20,13 +20,14 @@
#define LOG_TAG "AudioSystem-JNI"
#include <utils/Log.h>
+#include <sstream>
#include <jni.h>
#include <JNIHelp.h>
#include "core_jni_helpers.h"
#include <media/AudioSystem.h>
#include <media/AudioPolicy.h>
-
+#include <nativehelper/ScopedLocalRef.h>
#include <system/audio.h>
#include <system/audio_policy.h>
#include "android_media_AudioFormat.h"
@@ -903,6 +904,12 @@
return false; // not found
}
+// TODO: pull out to separate file
+template <typename T, size_t N>
+static constexpr size_t array_size(const T (&)[N]) {
+ return N;
+}
+
static jint convertAudioPortFromNative(JNIEnv *env,
jobject *jAudioPort, const struct audio_port *nAudioPort)
{
@@ -923,6 +930,38 @@
ALOGV("convertAudioPortFromNative id %d role %d type %d name %s",
nAudioPort->id, nAudioPort->role, nAudioPort->type, nAudioPort->name);
+ // Verify audio port array count info.
+ if (nAudioPort->num_sample_rates > array_size(nAudioPort->sample_rates)
+ || nAudioPort->num_channel_masks > array_size(nAudioPort->channel_masks)
+ || nAudioPort->num_formats > array_size(nAudioPort->formats)
+ || nAudioPort->num_gains > array_size(nAudioPort->gains)) {
+
+ std::stringstream ss;
+ ss << "convertAudioPortFromNative array count out of bounds:"
+ << " num_sample_rates " << nAudioPort->num_sample_rates
+ << " num_channel_masks " << nAudioPort->num_channel_masks
+ << " num_formats " << nAudioPort->num_formats
+ << " num_gains " << nAudioPort->num_gains
+ ;
+ std::string s = ss.str();
+
+ // Prefer to log through Java wtf instead of native ALOGE.
+ ScopedLocalRef<jclass> jLogClass(env, env->FindClass("android/util/Log"));
+ jmethodID jWtfId = (jLogClass.get() == nullptr)
+ ? nullptr
+ : env->GetStaticMethodID(jLogClass.get(), "wtf",
+ "(Ljava/lang/String;Ljava/lang/String;)I");
+ if (jWtfId != nullptr) {
+ ScopedLocalRef<jstring> jMessage(env, env->NewStringUTF(s.c_str()));
+ ScopedLocalRef<jstring> jTag(env, env->NewStringUTF(LOG_TAG));
+ (void)env->CallStaticIntMethod(jLogClass.get(), jWtfId, jTag.get(), jMessage.get());
+ } else {
+ ALOGE("%s", s.c_str());
+ }
+ jStatus = (jint)AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+
jSamplingRates = env->NewIntArray(nAudioPort->num_sample_rates);
if (jSamplingRates == NULL) {
jStatus = (jint)AUDIO_JAVA_ERROR;
@@ -1066,7 +1105,7 @@
&jAudioPortConfig,
&nAudioPort->active_config);
if (jStatus != AUDIO_JAVA_SUCCESS) {
- return jStatus;
+ goto exit;
}
env->SetObjectField(*jAudioPort, gAudioPortFields.mActiveConfig, jAudioPortConfig);
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 2364787..679e882 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -125,6 +125,99 @@
}
}
+static void android_net_utils_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
+ jint ifIndex)
+{
+ static const int kLinkLocalHopLimit = 255;
+
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+
+ // Set an ICMPv6 filter that only passes Router Solicitations.
+ struct icmp6_filter rs_only;
+ ICMP6_FILTER_SETBLOCKALL(&rs_only);
+ ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &rs_only);
+ socklen_t len = sizeof(rs_only);
+ if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &rs_only, len) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(ICMP6_FILTER): %s", strerror(errno));
+ return;
+ }
+
+ // Most/all of the rest of these options can be set via Java code, but
+ // because we're here on account of setting an icmp6_filter go ahead
+ // and do it all natively for now.
+ //
+ // TODO: Consider moving these out to Java.
+
+ // Set the multicast hoplimit to 255 (link-local only).
+ int hops = kLinkLocalHopLimit;
+ len = sizeof(hops);
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, len) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(IPV6_MULTICAST_HOPS): %s", strerror(errno));
+ return;
+ }
+
+ // Set the unicast hoplimit to 255 (link-local only).
+ hops = kLinkLocalHopLimit;
+ len = sizeof(hops);
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, len) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(IPV6_UNICAST_HOPS): %s", strerror(errno));
+ return;
+ }
+
+ // Explicitly disable multicast loopback.
+ int off = 0;
+ len = sizeof(off);
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, len) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno));
+ return;
+ }
+
+ // Specify the IPv6 interface to use for outbound multicast.
+ len = sizeof(ifIndex);
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIndex, len) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(IPV6_MULTICAST_IF): %s", strerror(errno));
+ return;
+ }
+
+ // Additional options to be considered:
+ // - IPV6_TCLASS
+ // - IPV6_RECVPKTINFO
+ // - IPV6_RECVHOPLIMIT
+
+ // Bind to [::].
+ const struct sockaddr_in6 sin6 = {
+ .sin6_family = AF_INET6,
+ .sin6_port = 0,
+ .sin6_flowinfo = 0,
+ .sin6_addr = IN6ADDR_ANY_INIT,
+ .sin6_scope_id = 0,
+ };
+ auto sa = reinterpret_cast<const struct sockaddr *>(&sin6);
+ len = sizeof(sin6);
+ if (bind(fd, sa, len) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "bind(IN6ADDR_ANY): %s", strerror(errno));
+ return;
+ }
+
+ // Join the all-routers multicast group, ff02::2%index.
+ struct ipv6_mreq all_rtrs = {
+ .ipv6mr_multiaddr = {{{0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2}}},
+ .ipv6mr_interface = ifIndex,
+ };
+ len = sizeof(all_rtrs);
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &all_rtrs, len) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(IPV6_JOIN_GROUP): %s", strerror(errno));
+ return;
+ }
+}
+
static jboolean android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId)
{
return (jboolean) !setNetworkForProcess(netId);
@@ -173,6 +266,7 @@
{ "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
{ "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter },
{ "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter },
+ { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_setupRaSocket },
};
int register_android_net_NetworkUtils(JNIEnv* env)
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 3d952b0..3c72274 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -58,21 +58,19 @@
#endif
// For both of these, err should be in the errno range (positive), not a status_t (negative)
-
-static void signalExceptionForPriorityError(JNIEnv* env, int err)
-{
+static void signalExceptionForError(JNIEnv* env, int err, int tid) {
switch (err) {
case EINVAL:
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "Invalid argument: %d", tid);
break;
case ESRCH:
- jniThrowException(env, "java/lang/IllegalArgumentException", "Given thread does not exist");
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "Given thread %d does not exist", tid);
break;
case EPERM:
- jniThrowException(env, "java/lang/SecurityException", "No permission to modify given thread");
- break;
- case EACCES:
- jniThrowException(env, "java/lang/SecurityException", "No permission to set to given priority");
+ jniThrowExceptionFmt(env, "java/lang/SecurityException",
+ "No permission to modify given thread %d", tid);
break;
default:
jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
@@ -80,23 +78,27 @@
}
}
-static void signalExceptionForGroupError(JNIEnv* env, int err)
-{
+static void signalExceptionForPriorityError(JNIEnv* env, int err, int tid) {
switch (err) {
- case EINVAL:
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- break;
- case ESRCH:
- jniThrowException(env, "java/lang/IllegalArgumentException", "Given thread does not exist");
- break;
- case EPERM:
- jniThrowException(env, "java/lang/SecurityException", "No permission to modify given thread");
- break;
case EACCES:
- jniThrowException(env, "java/lang/SecurityException", "No permission to set to given group");
+ jniThrowExceptionFmt(env, "java/lang/SecurityException",
+ "No permission to set the priority of %d", tid);
break;
default:
- jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
+ signalExceptionForError(env, err, tid);
+ break;
+ }
+
+}
+
+static void signalExceptionForGroupError(JNIEnv* env, int err, int tid) {
+ switch (err) {
+ case EACCES:
+ jniThrowExceptionFmt(env, "java/lang/SecurityException",
+ "No permission to set the group of %d", tid);
+ break;
+ default:
+ signalExceptionForError(env, err, tid);
break;
}
}
@@ -171,7 +173,7 @@
SchedPolicy sp = (SchedPolicy) grp;
int res = set_sched_policy(tid, sp);
if (res != NO_ERROR) {
- signalExceptionForGroupError(env, -res);
+ signalExceptionForGroupError(env, -res, tid);
}
}
@@ -183,7 +185,7 @@
struct dirent *de;
if ((grp == SP_FOREGROUND) || (grp > SP_MAX)) {
- signalExceptionForGroupError(env, EINVAL);
+ signalExceptionForGroupError(env, EINVAL, pid);
return;
}
@@ -219,7 +221,7 @@
if (!(d = opendir(proc_path))) {
// If the process exited on us, don't generate an exception
if (errno != ENOENT)
- signalExceptionForGroupError(env, errno);
+ signalExceptionForGroupError(env, errno, pid);
return;
}
@@ -254,7 +256,7 @@
#ifdef ENABLE_CPUSETS
int err = set_cpuset_policy(t_pid, sp);
if (err != NO_ERROR) {
- signalExceptionForGroupError(env, -err);
+ signalExceptionForGroupError(env, -err, t_pid);
break;
}
#endif
@@ -266,14 +268,14 @@
// set both cpuset and cgroup for general threads
err = set_cpuset_policy(t_pid, sp);
if (err != NO_ERROR) {
- signalExceptionForGroupError(env, -err);
+ signalExceptionForGroupError(env, -err, t_pid);
break;
}
#endif
err = set_sched_policy(t_pid, sp);
if (err != NO_ERROR) {
- signalExceptionForGroupError(env, -err);
+ signalExceptionForGroupError(env, -err, t_pid);
break;
}
@@ -285,7 +287,7 @@
{
SchedPolicy sp;
if (get_sched_policy(pid, &sp) != 0) {
- signalExceptionForGroupError(env, errno);
+ signalExceptionForGroupError(env, errno, pid);
}
return (int) sp;
}
@@ -400,7 +402,7 @@
jintArray cpus;
int pid = getpid();
if (get_sched_policy(pid, &sp) != 0) {
- signalExceptionForGroupError(env, errno);
+ signalExceptionForGroupError(env, errno, pid);
return NULL;
}
get_exclusive_cpuset_cores(sp, &cpu_set);
@@ -440,6 +442,23 @@
#endif
}
+jint android_os_Process_getThreadScheduler(JNIEnv* env, jclass clazz,
+ jint tid)
+{
+ int policy = 0;
+// linux has sched_getscheduler(), others don't.
+#if defined(__linux__)
+ errno = 0;
+ policy = sched_getscheduler(tid);
+ if (errno != 0) {
+ signalExceptionForPriorityError(env, errno, tid);
+ }
+#else
+ signalExceptionForPriorityError(env, ENOSYS, tid);
+#endif
+ return policy;
+}
+
void android_os_Process_setThreadScheduler(JNIEnv* env, jclass clazz,
jint tid, jint policy, jint pri)
{
@@ -449,10 +468,10 @@
param.sched_priority = pri;
int rc = sched_setscheduler(tid, policy, ¶m);
if (rc) {
- signalExceptionForPriorityError(env, errno);
+ signalExceptionForPriorityError(env, errno, tid);
}
#else
- signalExceptionForPriorityError(env, ENOSYS);
+ signalExceptionForPriorityError(env, ENOSYS, tid);
#endif
}
@@ -477,9 +496,9 @@
int rc = androidSetThreadPriority(pid, pri);
if (rc != 0) {
if (rc == INVALID_OPERATION) {
- signalExceptionForPriorityError(env, errno);
+ signalExceptionForPriorityError(env, errno, pid);
} else {
- signalExceptionForGroupError(env, errno);
+ signalExceptionForGroupError(env, errno, pid);
}
}
@@ -499,7 +518,7 @@
errno = 0;
jint pri = getpriority(PRIO_PROCESS, pid);
if (errno != 0) {
- signalExceptionForPriorityError(env, errno);
+ signalExceptionForPriorityError(env, errno, pid);
}
//ALOGI("Returning priority of %" PRId32 ": %" PRId32 "\n", pid, pri);
return pri;
@@ -1191,6 +1210,7 @@
{"setCanSelfBackground", "(Z)V", (void*)android_os_Process_setCanSelfBackground},
{"setThreadPriority", "(I)V", (void*)android_os_Process_setCallingThreadPriority},
{"getThreadPriority", "(I)I", (void*)android_os_Process_getThreadPriority},
+ {"getThreadScheduler", "(I)I", (void*)android_os_Process_getThreadScheduler},
{"setThreadGroup", "(II)V", (void*)android_os_Process_setThreadGroup},
{"setProcessGroup", "(II)V", (void*)android_os_Process_setProcessGroup},
{"getProcessGroup", "(I)I", (void*)android_os_Process_getProcessGroup},
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 4fc546c..b0028e1 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -573,8 +573,9 @@
bounds.roundOut();
}
+ incStrong(0);
auto functor = std::bind(
- std::mem_fn(&SurfaceViewPositionUpdater::doUpdatePosition), this,
+ std::mem_fn(&SurfaceViewPositionUpdater::doUpdatePositionAsync), this,
(jlong) info.canvasContext.getFrameNumber(),
(jint) bounds.left, (jint) bounds.top,
(jint) bounds.right, (jint) bounds.bottom);
@@ -585,15 +586,18 @@
virtual void onPositionLost(RenderNode& node, const TreeInfo* info) override {
if (CC_UNLIKELY(!mWeakRef || (info && !info->updateWindowPositions))) return;
- if (info) {
- auto functor = std::bind(
- std::mem_fn(&SurfaceViewPositionUpdater::doNotifyPositionLost), this,
- (jlong) info->canvasContext.getFrameNumber());
-
- info->canvasContext.enqueueFrameWork(std::move(functor));
- } else {
- doNotifyPositionLost(0);
+ ATRACE_NAME("SurfaceView position lost");
+ JNIEnv* env = jnienv();
+ jobject localref = env->NewLocalRef(mWeakRef);
+ if (CC_UNLIKELY(!localref)) {
+ jnienv()->DeleteWeakGlobalRef(mWeakRef);
+ mWeakRef = nullptr;
+ return;
}
+
+ env->CallVoidMethod(localref, gSurfaceViewPositionLostMethod,
+ info ? info->canvasContext.getFrameNumber() : 0);
+ env->DeleteLocalRef(localref);
}
private:
@@ -605,36 +609,23 @@
return env;
}
- void doUpdatePosition(jlong frameNumber, jint left, jint top,
+ void doUpdatePositionAsync(jlong frameNumber, jint left, jint top,
jint right, jint bottom) {
ATRACE_NAME("Update SurfaceView position");
JNIEnv* env = jnienv();
jobject localref = env->NewLocalRef(mWeakRef);
if (CC_UNLIKELY(!localref)) {
- jnienv()->DeleteWeakGlobalRef(mWeakRef);
+ env->DeleteWeakGlobalRef(mWeakRef);
mWeakRef = nullptr;
- return;
+ } else {
+ env->CallVoidMethod(localref, gSurfaceViewPositionUpdateMethod,
+ frameNumber, left, top, right, bottom);
+ env->DeleteLocalRef(localref);
}
- env->CallVoidMethod(localref, gSurfaceViewPositionUpdateMethod,
- frameNumber, left, top, right, bottom);
- env->DeleteLocalRef(localref);
- }
-
- void doNotifyPositionLost(jlong frameNumber) {
- ATRACE_NAME("SurfaceView position lost");
-
- JNIEnv* env = jnienv();
- jobject localref = env->NewLocalRef(mWeakRef);
- if (CC_UNLIKELY(!localref)) {
- jnienv()->DeleteWeakGlobalRef(mWeakRef);
- mWeakRef = nullptr;
- return;
- }
-
- env->CallVoidMethod(localref, gSurfaceViewPositionLostMethod, frameNumber);
- env->DeleteLocalRef(localref);
+ // We need to release ourselves here
+ decStrong(0);
}
JavaVM* mVm;
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index ff75677..0d8a95c 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -32,6 +32,7 @@
#include <jni.h>
#include <memory>
#include <stdio.h>
+#include <system/graphics.h>
#include <ui/DisplayInfo.h>
#include <ui/HdrCapabilities.h>
#include <ui/FrameStats.h>
@@ -58,7 +59,6 @@
jfieldID secure;
jfieldID appVsyncOffsetNanos;
jfieldID presentationDeadlineNanos;
- jfieldID colorTransform;
} gPhysicalDisplayInfoClassInfo;
static struct {
@@ -248,10 +248,10 @@
}
}
-static void nativeSetPositionAppliesWithResize(JNIEnv* env, jclass clazz,
+static void nativeSetGeometryAppliesWithResize(JNIEnv* env, jclass clazz,
jlong nativeObject) {
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
- status_t err = ctrl->setPositionAppliesWithResize();
+ status_t err = ctrl->setGeometryAppliesWithResize();
if (err < 0 && err != NO_INIT) {
doThrowIAE(env);
}
@@ -429,8 +429,6 @@
info.appVsyncOffset);
env->SetLongField(infoObj, gPhysicalDisplayInfoClassInfo.presentationDeadlineNanos,
info.presentationDeadline);
- env->SetIntField(infoObj, gPhysicalDisplayInfoClassInfo.colorTransform,
- info.colorTransform);
env->SetObjectArrayElement(configArray, static_cast<jsize>(c), infoObj);
env->DeleteLocalRef(infoObj);
}
@@ -451,6 +449,43 @@
return err == NO_ERROR ? JNI_TRUE : JNI_FALSE;
}
+static jintArray nativeGetDisplayColorModes(JNIEnv* env, jclass, jobject tokenObj) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+ if (token == NULL) return NULL;
+ Vector<android_color_mode_t> colorModes;
+ if (SurfaceComposerClient::getDisplayColorModes(token, &colorModes) != NO_ERROR ||
+ colorModes.isEmpty()) {
+ return NULL;
+ }
+
+ jintArray colorModesArray = env->NewIntArray(colorModes.size());
+ if (colorModesArray == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ return NULL;
+ }
+ jint* colorModesArrayValues = env->GetIntArrayElements(colorModesArray, 0);
+ for (size_t i = 0; i < colorModes.size(); i++) {
+ colorModesArrayValues[i] = static_cast<jint>(colorModes[i]);
+ }
+ env->ReleaseIntArrayElements(colorModesArray, colorModesArrayValues, 0);
+ return colorModesArray;
+}
+
+static jint nativeGetActiveColorMode(JNIEnv* env, jclass, jobject tokenObj) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+ if (token == NULL) return -1;
+ return static_cast<jint>(SurfaceComposerClient::getActiveColorMode(token));
+}
+
+static jboolean nativeSetActiveColorMode(JNIEnv* env, jclass,
+ jobject tokenObj, jint colorMode) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+ if (token == NULL) return JNI_FALSE;
+ status_t err = SurfaceComposerClient::setActiveColorMode(token,
+ static_cast<android_color_mode_t>(colorMode));
+ return err == NO_ERROR ? JNI_TRUE : JNI_FALSE;
+}
+
static void nativeSetDisplayPowerMode(JNIEnv* env, jclass clazz, jobject tokenObj, jint mode) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == NULL) return;
@@ -626,6 +661,16 @@
return javaObjectForIBinder(env, ctrl->getHandle());
}
+static jboolean nativeGetTransformToDisplayInverse(JNIEnv* env, jclass clazz, jlong nativeObject) {
+ bool out = false;
+ auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+ status_t status = ctrl->getTransformToDisplayInverse(&out);
+ if (status != NO_ERROR) {
+ return false;
+ }
+ return out;
+}
+
static jobject nativeGetHdrCapabilities(JNIEnv* env, jclass clazz, jobject tokenObject) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
if (token == NULL) return NULL;
@@ -667,8 +712,8 @@
(void*)nativeSetLayer },
{"nativeSetPosition", "(JFF)V",
(void*)nativeSetPosition },
- {"nativeSetPositionAppliesWithResize", "(J)V",
- (void*)nativeSetPositionAppliesWithResize },
+ {"nativeSetGeometryAppliesWithResize", "(J)V",
+ (void*)nativeSetGeometryAppliesWithResize },
{"nativeSetSize", "(JII)V",
(void*)nativeSetSize },
{"nativeSetTransparentRegionHint", "(JLandroid/graphics/Region;)V",
@@ -705,6 +750,12 @@
(void*)nativeGetActiveConfig },
{"nativeSetActiveConfig", "(Landroid/os/IBinder;I)Z",
(void*)nativeSetActiveConfig },
+ {"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I",
+ (void*)nativeGetDisplayColorModes},
+ {"nativeGetActiveColorMode", "(Landroid/os/IBinder;)I",
+ (void*)nativeGetActiveColorMode},
+ {"nativeSetActiveColorMode", "(Landroid/os/IBinder;I)Z",
+ (void*)nativeSetActiveColorMode},
{"nativeGetHdrCapabilities", "(Landroid/os/IBinder;)Landroid/view/Display$HdrCapabilities;",
(void*)nativeGetHdrCapabilities },
{"nativeClearContentFrameStats", "(J)Z",
@@ -722,7 +773,9 @@
{"nativeSetOverrideScalingMode", "(JI)V",
(void*)nativeSetOverrideScalingMode },
{"nativeGetHandle", "(J)Landroid/os/IBinder;",
- (void*)nativeGetHandle }
+ (void*)nativeGetHandle },
+ {"nativeGetTransformToDisplayInverse", "(J)Z",
+ (void*)nativeGetTransformToDisplayInverse },
};
int register_android_view_SurfaceControl(JNIEnv* env)
@@ -745,8 +798,6 @@
clazz, "appVsyncOffsetNanos", "J");
gPhysicalDisplayInfoClassInfo.presentationDeadlineNanos = GetFieldIDOrDie(env,
clazz, "presentationDeadlineNanos", "J");
- gPhysicalDisplayInfoClassInfo.colorTransform = GetFieldIDOrDie(env, clazz,
- "colorTransform", "I");
jclass rectClazz = FindClassOrDie(env, "android/graphics/Rect");
gRectClassInfo.bottom = GetFieldIDOrDie(env, rectClazz, "bottom", "I");
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 5b4bfe9..7cd0d2a 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -32,6 +32,7 @@
#include <utils/Looper.h>
#include <utils/RefBase.h>
#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
#include <android_runtime/android_view_Surface.h>
#include <system/window.h>
@@ -44,6 +45,7 @@
#include <FrameMetricsObserver.h>
#include <IContextFactory.h>
#include <JankTracker.h>
+#include <PropertyValuesAnimatorSet.h>
#include <RenderNode.h>
#include <renderthread/CanvasContext.h>
#include <renderthread/RenderProxy.h>
@@ -122,6 +124,31 @@
std::vector<OnFinishedEvent> mOnFinishedEvents;
};
+class FinishAndInvokeListener : public MessageHandler {
+public:
+ explicit FinishAndInvokeListener(PropertyValuesAnimatorSet* anim)
+ : mAnimator(anim) {
+ mListener = anim->getOneShotListener();
+ mRequestId = anim->getRequestId();
+ }
+
+ virtual void handleMessage(const Message& message) {
+ if (mAnimator->getRequestId() == mRequestId) {
+ // Request Id has not changed, meaning there's no animation lifecyle change since the
+ // message is posted, so go ahead and call finish to make sure the PlayState is properly
+ // updated. This is needed because before the next frame comes in from UI thread to
+ // trigger an animation update, there could be reverse/cancel etc. So we need to update
+ // the playstate in time to ensure all the subsequent events get chained properly.
+ mAnimator->end();
+ }
+ mListener->onAnimationFinished(nullptr);
+ }
+private:
+ sp<PropertyValuesAnimatorSet> mAnimator;
+ sp<AnimationListener> mListener;
+ uint32_t mRequestId;
+};
+
class RenderingException : public MessageHandler {
public:
RenderingException(JavaVM* vm, const std::string& message)
@@ -160,6 +187,23 @@
virtual void prepareTree(TreeInfo& info) override {
info.errorHandler = this;
+
+ for (auto& anim : mRunningVDAnimators) {
+ // Assume that the property change in VD from the animators will not be consumed. Mark
+ // otherwise if the VDs are found in the display list tree. For VDs that are not in
+ // the display list tree, we stop providing animation pulses by 1) removing them from
+ // the animation list, 2) post a delayed message to end them at end time so their
+ // listeners can receive the corresponding callbacks.
+ anim->getVectorDrawable()->setPropertyChangeWillBeConsumed(false);
+ // Mark the VD dirty so it will damage itself during prepareTree.
+ anim->getVectorDrawable()->markDirty();
+ }
+ if (info.mode == TreeInfo::MODE_FULL) {
+ for (auto &anim : mPausedVDAnimators) {
+ anim->getVectorDrawable()->setPropertyChangeWillBeConsumed(false);
+ anim->getVectorDrawable()->markDirty();
+ }
+ }
// TODO: This is hacky
info.windowInsetLeft = -stagingProperties().getLeft();
info.windowInsetTop = -stagingProperties().getTop();
@@ -175,10 +219,39 @@
mLooper->sendMessage(handler, 0);
}
+ void sendMessageDelayed(const sp<MessageHandler>& handler, nsecs_t delayInMs) {
+ mLooper->sendMessageDelayed(ms2ns(delayInMs), handler, 0);
+ }
+
void attachAnimatingNode(RenderNode* animatingNode) {
mPendingAnimatingRenderNodes.push_back(animatingNode);
}
+ void attachPendingVectorDrawableAnimators() {
+ mRunningVDAnimators.insert(mPendingVectorDrawableAnimators.begin(),
+ mPendingVectorDrawableAnimators.end());
+ mPendingVectorDrawableAnimators.clear();
+ }
+
+ void detachAnimators() {
+ // Remove animators from the list and post a delayed message in future to end the animator
+ for (auto& anim : mRunningVDAnimators) {
+ detachVectorDrawableAnimator(anim.get());
+ }
+ mRunningVDAnimators.clear();
+ mPausedVDAnimators.clear();
+ }
+
+ // Move all the animators to the paused list, and send a delayed message to notify the finished
+ // listener.
+ void pauseAnimators() {
+ mPausedVDAnimators.insert(mRunningVDAnimators.begin(), mRunningVDAnimators.end());
+ for (auto& anim : mRunningVDAnimators) {
+ detachVectorDrawableAnimator(anim.get());
+ }
+ mRunningVDAnimators.clear();
+ }
+
void doAttachAnimatingNodes(AnimationContext* context) {
for (size_t i = 0; i < mPendingAnimatingRenderNodes.size(); i++) {
RenderNode* node = mPendingAnimatingRenderNodes[i].get();
@@ -187,17 +260,144 @@
mPendingAnimatingRenderNodes.clear();
}
+ // Run VectorDrawable animators after prepareTree.
+ void runVectorDrawableAnimators(AnimationContext* context, TreeInfo& info) {
+ // Push staging.
+ if (info.mode == TreeInfo::MODE_FULL) {
+ pushStagingVectorDrawableAnimators(context);
+ }
+
+ // Run the animators in the running list.
+ for (auto it = mRunningVDAnimators.begin(); it != mRunningVDAnimators.end();) {
+ if ((*it)->animate(*context)) {
+ it = mRunningVDAnimators.erase(it);
+ } else {
+ it++;
+ }
+ }
+
+ // Run the animators in paused list during full sync.
+ if (info.mode == TreeInfo::MODE_FULL) {
+ // During full sync we also need to pulse paused animators, in case their targets
+ // have been added back to the display list. All the animators that passed the
+ // scheduled finish time will be removed from the paused list.
+ for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) {
+ if ((*it)->animate(*context)) {
+ // Animator has finished, remove from the list.
+ it = mPausedVDAnimators.erase(it);
+ } else {
+ it++;
+ }
+ }
+ }
+
+ // Move the animators with a target not in DisplayList to paused list.
+ for (auto it = mRunningVDAnimators.begin(); it != mRunningVDAnimators.end();) {
+ if (!(*it)->getVectorDrawable()->getPropertyChangeWillBeConsumed()) {
+ // Vector Drawable is not in the display list, we should remove this animator from
+ // the list, put it in the paused list, and post a delayed message to end the
+ // animator.
+ detachVectorDrawableAnimator(it->get());
+ mPausedVDAnimators.insert(*it);
+ it = mRunningVDAnimators.erase(it);
+ } else {
+ it++;
+ }
+ }
+
+ // Move the animators with a target in DisplayList from paused list to running list, and
+ // trim paused list.
+ if (info.mode == TreeInfo::MODE_FULL) {
+ // Check whether any paused animator's target is back in Display List. If so, put the
+ // animator back in the running list.
+ for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) {
+ if ((*it)->getVectorDrawable()->getPropertyChangeWillBeConsumed()) {
+ mRunningVDAnimators.insert(*it);
+ it = mPausedVDAnimators.erase(it);
+ } else {
+ it++;
+ }
+ }
+ // Trim paused VD animators at full sync, so that when Java loses reference to an
+ // animator, we know we won't be requested to animate it any more, then we remove such
+ // animators from the paused list so they can be properly freed. We also remove the
+ // animators from paused list when the time elapsed since start has exceeded duration.
+ trimPausedVDAnimators(context);
+ }
+
+ info.out.hasAnimations |= !mRunningVDAnimators.empty();
+ }
+
+ void trimPausedVDAnimators(AnimationContext* context) {
+ // Trim paused vector drawable animator list.
+ for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) {
+ // Remove paused VD animator if no one else is referencing it. Note that animators that
+ // have passed scheduled finish time are removed from list when they are being pulsed
+ // before prepare tree.
+ // TODO: this is a bit hacky, need to figure out a better way to track when the paused
+ // animators should be freed.
+ if ((*it)->getStrongCount() == 1) {
+ it = mPausedVDAnimators.erase(it);
+ } else {
+ it++;
+ }
+ }
+ }
+
+ void pushStagingVectorDrawableAnimators(AnimationContext* context) {
+ for (auto& anim : mRunningVDAnimators) {
+ anim->pushStaging(*context);
+ }
+ }
+
void destroy() {
for (auto& renderNode : mPendingAnimatingRenderNodes) {
renderNode->animators().endAllStagingAnimators();
}
mPendingAnimatingRenderNodes.clear();
+ mPendingVectorDrawableAnimators.clear();
+ }
+
+ void addVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) {
+ mPendingVectorDrawableAnimators.insert(anim);
}
private:
sp<Looper> mLooper;
JavaVM* mVm;
std::vector< sp<RenderNode> > mPendingAnimatingRenderNodes;
+ std::set< sp<PropertyValuesAnimatorSet> > mPendingVectorDrawableAnimators;
+ std::set< sp<PropertyValuesAnimatorSet> > mRunningVDAnimators;
+ // mPausedVDAnimators stores a list of animators that have not yet passed the finish time, but
+ // their VectorDrawable targets are no longer in the DisplayList. We skip these animators when
+ // render thread runs animators independent of UI thread (i.e. RT_ONLY mode). These animators
+ // need to be re-activated once their VD target is added back into DisplayList. Since that could
+ // only happen when we do a full sync, we need to make sure to pulse these paused animators at
+ // full sync. If any animator's VD target is found in DisplayList during a full sync, we move
+ // the animator back to the running list.
+ std::set< sp<PropertyValuesAnimatorSet> > mPausedVDAnimators;
+ void detachVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) {
+ if (anim->isInfinite() || !anim->isRunning()) {
+ // Do not need to post anything if the animation is infinite (i.e. no meaningful
+ // end listener action), or if the animation has already ended.
+ return;
+ }
+ nsecs_t remainingTimeInMs = anim->getRemainingPlayTime();
+ // Post a delayed onFinished event that is scheduled to be handled when the animator ends.
+ if (anim->getOneShotListener()) {
+ // VectorDrawable's oneshot listener is updated when there are user triggered animation
+ // lifecycle changes, such as start(), end(), etc. By using checking and clearing
+ // one shot listener, we ensure the same end listener event gets posted only once.
+ // Therefore no duplicates. Another benefit of using one shot listener is that no
+ // removal is necessary: the end time of animation will not change unless triggered by
+ // user events, in which case the already posted listener's id will become stale, and
+ // the onFinished callback will then be ignored.
+ sp<FinishAndInvokeListener> message
+ = new FinishAndInvokeListener(anim);
+ sendMessageDelayed(message, remainingTimeInMs);
+ anim->clearOneShotListener();
+ }
+ }
};
class AnimationContextBridge : public AnimationContext {
@@ -213,6 +413,7 @@
virtual void startFrame(TreeInfo::TraversalMode mode) {
if (mode == TreeInfo::MODE_FULL) {
mRootNode->doAttachAnimatingNodes(this);
+ mRootNode->attachPendingVectorDrawableAnimators();
}
AnimationContext::startFrame(mode);
}
@@ -220,9 +421,14 @@
// Runs any animations still left in mCurrentFrameAnimations
virtual void runRemainingAnimations(TreeInfo& info) {
AnimationContext::runRemainingAnimations(info);
+ mRootNode->runVectorDrawableAnimators(this, info);
postOnFinishedEvents();
}
+ virtual void pauseAnimators() override {
+ mRootNode->pauseAnimators();
+ }
+
virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) {
OnFinishedEvent event(animator, listener);
mOnFinishedEvents.push_back(event);
@@ -230,6 +436,7 @@
virtual void destroy() {
AnimationContext::destroy();
+ mRootNode->detachAnimators();
postOnFinishedEvents();
}
@@ -416,6 +623,12 @@
proxy->setProcessStatsBuffer(fd);
}
+static jint android_view_ThreadedRenderer_getRenderThreadTid(JNIEnv* env, jobject clazz,
+ jlong proxyPtr) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ return proxy->getRenderThreadTid();
+}
+
static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
RootRenderNode* node = new RootRenderNode(env);
node->incStrong(0);
@@ -528,6 +741,13 @@
rootRenderNode->attachAnimatingNode(animatingNode);
}
+static void android_view_ThreadedRenderer_registerVectorDrawableAnimator(JNIEnv* env, jobject clazz,
+ jlong rootNodePtr, jlong animatorPtr) {
+ RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr);
+ PropertyValuesAnimatorSet* animator = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorPtr);
+ rootRenderNode->addVectorDrawableAnimator(animator);
+}
+
static void android_view_ThreadedRenderer_invokeFunctor(JNIEnv* env, jobject clazz,
jlong functorPtr, jboolean waitForCompletion) {
Functor* functor = reinterpret_cast<Functor*>(functorPtr);
@@ -724,6 +944,7 @@
static const JNINativeMethod gMethods[] = {
{ "nSetAtlas", "(JLandroid/view/GraphicBuffer;[J)V", (void*) android_view_ThreadedRenderer_setAtlas },
{ "nSetProcessStatsBuffer", "(JI)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer },
+ { "nGetRenderThreadTid", "(J)I", (void*) android_view_ThreadedRenderer_getRenderThreadTid },
{ "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode },
{ "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy },
{ "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy },
@@ -739,6 +960,7 @@
{ "nSyncAndDrawFrame", "(J[JI)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
{ "nDestroy", "(JJ)V", (void*) android_view_ThreadedRenderer_destroy },
{ "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode },
+ { "nRegisterVectorDrawableAnimator", "(JJ)V", (void*) android_view_ThreadedRenderer_registerVectorDrawableAnimator },
{ "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
{ "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer },
{ "nBuildLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_buildLayer },
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 3f4b2a6..a04fc2a 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -176,7 +176,9 @@
}
int rc = setgroups(gids.size(), reinterpret_cast<const gid_t*>(&gids[0]));
if (rc == -1) {
- RuntimeAbort(env, __LINE__, "setgroups failed");
+ std::ostringstream oss;
+ oss << "setgroups failed: " << strerror(errno) << ", gids.size=" << gids.size();
+ RuntimeAbort(env, __LINE__, oss.str().c_str());
}
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e9a3409..ed71fc2f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -49,6 +49,7 @@
<protected-broadcast android:name="android.intent.action.PACKAGE_VERIFIED" />
<protected-broadcast android:name="android.intent.action.PACKAGES_SUSPENDED" />
<protected-broadcast android:name="android.intent.action.PACKAGES_UNSUSPENDED" />
+ <protected-broadcast android:name="android.intent.action.ACTION_PREFERRED_ACTIVITY_CHANGED" />
<protected-broadcast android:name="android.intent.action.UID_REMOVED" />
<protected-broadcast android:name="android.intent.action.QUERY_PACKAGE_RESTART" />
<protected-broadcast android:name="android.intent.action.CONFIGURATION_CHANGED" />
@@ -193,6 +194,7 @@
<protected-broadcast android:name="android.btopp.intent.action.OPEN" />
<protected-broadcast android:name="android.btopp.intent.action.OPEN_INBOUND" />
<protected-broadcast android:name="android.btopp.intent.action.TRANSFER_COMPLETE" />
+ <protected-broadcast android:name="com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN" />
<protected-broadcast android:name="com.android.bluetooth.pbap.authchall" />
<protected-broadcast android:name="com.android.bluetooth.pbap.userconfirmtimeout" />
<protected-broadcast android:name="com.android.bluetooth.pbap.authresponse" />
@@ -469,12 +471,6 @@
<protected-broadcast android:name="android.net.wifi.PASSPOINT_ICON_RECEIVED" />
<protected-broadcast android:name="com.android.server.notification.CountdownConditionProvider" />
- <protected-broadcast android:name="com.android.ims.IMS_SERVICE_UP" />
- <protected-broadcast android:name="com.android.ims.IMS_INCOMING_CALL" />
- <protected-broadcast android:name="com.android.ims.internal.uce.UCE_SERVICE_UP" />
- <protected-broadcast android:name="com.android.intent.action.IMS_FEATURE_CHANGED" />
- <protected-broadcast android:name="com.android.intent.action.IMS_CONFIG_CHANGED" />
-
<protected-broadcast android:name="com.android.internal.location.ALARM_WAKEUP" />
<protected-broadcast android:name="com.android.internal.location.ALARM_TIMEOUT" />
<protected-broadcast android:name="android.intent.action.GLOBAL_BUTTON" />
@@ -483,6 +479,8 @@
<protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_UNAVAILABLE" />
<protected-broadcast android:name="com.android.server.pm.DISABLE_QUIET_MODE_AFTER_UNLOCK" />
+ <protected-broadcast android:name="com.android.server.retaildemo.ACTION_RESET_DEMO" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
@@ -1264,6 +1262,11 @@
<permission android:name="android.permission.CONNECTIVITY_INTERNAL"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an internal user to use restricted Networks.
+ @hide -->
+ <permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"
+ android:protectionLevel="signature|privileged" />
+
<!-- Allows a system application to access hardware packet offload capabilities.
@hide -->
<permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD"
@@ -2510,11 +2513,10 @@
<permission android:name="android.permission.CONTROL_WIFI_DISPLAY"
android:protectionLevel="signature" />
- <!-- Allows an application to control the color transforms applied to
- displays system-wide.
+ <!-- Allows an application to control the color modes set for displays system-wide.
<p>Not for use by third-party applications.</p>
@hide -->
- <permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_TRANSFORM"
+ <permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE"
android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to control VPN.
diff --git a/core/res/res/anim/watch_switch_thumb_to_off_animation.xml b/core/res/res/anim/watch_switch_thumb_to_off_animation.xml
new file mode 100644
index 0000000..cd02e0d
--- /dev/null
+++ b/core/res/res/anim/watch_switch_thumb_to_off_animation.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?><!-- 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:ordering="sequentially">
+ <objectAnimator
+ android:duration="33"
+ android:interpolator="@android:interpolator/linear"
+ android:propertyName="pathData"
+ android:valueFrom="M 0.0,-7.0 l 0.0,0.0 c 3.8659932486,0.0 7.0,3.1340067514 7.0,7.0 l 0.0,0.0 c 0.0,3.8659932486 -3.1340067514,7.0 -7.0,7.0 l 0.0,0.0 c -3.8659932486,0.0 -7.0,-3.1340067514 -7.0,-7.0 l 0.0,0.0 c 0.0,-3.8659932486 3.1340067514,-7.0 7.0,-7.0 Z"
+ android:valueTo="M -3.0,-7.0 l 6.0,0.0 c 3.8659932486,0.0 7.0,3.1340067514 7.0,7.0 l 0.0,0.0 c 0.0,3.8659932486 -3.1340067514,7.0 -7.0,7.0 l -6.0,0.0 c -3.8659932486,0.0 -7.0,-3.1340067514 -7.0,-7.0 l 0.0,0.0 c 0.0,-3.8659932486 3.1340067514,-7.0 7.0,-7.0 Z"
+ android:valueType="pathType" />
+ <objectAnimator
+ android:duration="66"
+ android:interpolator="@android:interpolator/linear"
+ android:propertyName="pathData"
+ android:valueFrom="M -3.0,-7.0 l 6.0,0.0 c 3.8659932486,0.0 7.0,3.1340067514 7.0,7.0 l 0.0,0.0 c 0.0,3.8659932486 -3.1340067514,7.0 -7.0,7.0 l -6.0,0.0 c -3.8659932486,0.0 -7.0,-3.1340067514 -7.0,-7.0 l 0.0,0.0 c 0.0,-3.8659932486 3.1340067514,-7.0 7.0,-7.0 Z"
+ android:valueTo="M -3.0,-7.0 l 6.0,0.0 c 3.8659932486,0.0 7.0,3.1340067514 7.0,7.0 l 0.0,0.0 c 0.0,3.8659932486 -3.1340067514,7.0 -7.0,7.0 l -6.0,0.0 c -3.8659932486,0.0 -7.0,-3.1340067514 -7.0,-7.0 l 0.0,0.0 c 0.0,-3.8659932486 3.1340067514,-7.0 7.0,-7.0 Z"
+ android:valueType="pathType" />
+ <objectAnimator
+ android:duration="66"
+ android:interpolator="@android:interpolator/linear"
+ android:propertyName="pathData"
+ android:valueFrom="M -3.0,-7.0 l 6.0,0.0 c 3.8659932486,0.0 7.0,3.1340067514 7.0,7.0 l 0.0,0.0 c 0.0,3.8659932486 -3.1340067514,7.0 -7.0,7.0 l -6.0,0.0 c -3.8659932486,0.0 -7.0,-3.1340067514 -7.0,-7.0 l 0.0,0.0 c 0.0,-3.8659932486 3.1340067514,-7.0 7.0,-7.0 Z"
+ android:valueTo="M 0.0,-7.0 l 0.0,0.0 c 3.8659932486,0.0 7.0,3.1340067514 7.0,7.0 l 0.0,0.0 c 0.0,3.8659932486 -3.1340067514,7.0 -7.0,7.0 l 0.0,0.0 c -3.8659932486,0.0 -7.0,-3.1340067514 -7.0,-7.0 l 0.0,0.0 c 0.0,-3.8659932486 3.1340067514,-7.0 7.0,-7.0 Z"
+ android:valueType="pathType" />
+</set>
diff --git a/core/res/res/anim/watch_switch_thumb_to_on_animation.xml b/core/res/res/anim/watch_switch_thumb_to_on_animation.xml
new file mode 100644
index 0000000..e644217
--- /dev/null
+++ b/core/res/res/anim/watch_switch_thumb_to_on_animation.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:ordering="sequentially">
+ <objectAnimator
+ android:duration="33"
+ android:interpolator="@android:interpolator/linear"
+ android:propertyName="pathData"
+ android:valueFrom="M 0.0,-7.0 l 0.0,0.0 c 3.8659932486,0.0 7.0,3.1340067514 7.0,7.0 l 0.0,0.0 c 0.0,3.8659932486 -3.1340067514,7.0 -7.0,7.0 l 0.0,0.0 c -3.8659932486,0.0 -7.0,-3.1340067514 -7.0,-7.0 l 0.0,0.0 c 0.0,-3.8659932486 3.1340067514,-7.0 7.0,-7.0 Z"
+ android:valueTo="M -3.0,-7.0 l 6.0,0.0 c 3.8659932486,0.0 7.0,3.1340067514 7.0,7.0 l 0.0,0.0 c 0.0,3.8659932486 -3.1340067514,7.0 -7.0,7.0 l -6.0,0.0 c -3.8659932486,0.0 -7.0,-3.1340067514 -7.0,-7.0 l 0.0,0.0 c 0.0,-3.8659932486 3.1340067514,-7.0 7.0,-7.0 Z"
+ android:valueType="pathType" />
+ <objectAnimator
+ android:duration="66"
+ android:interpolator="@android:interpolator/linear"
+ android:propertyName="pathData"
+ android:valueFrom="M -3.0,-7.0 l 6.0,0.0 c 3.8659932486,0.0 7.0,3.1340067514 7.0,7.0 l 0.0,0.0 c 0.0,3.8659932486 -3.1340067514,7.0 -7.0,7.0 l -6.0,0.0 c -3.8659932486,0.0 -7.0,-3.1340067514 -7.0,-7.0 l 0.0,0.0 c 0.0,-3.8659932486 3.1340067514,-7.0 7.0,-7.0 Z"
+ android:valueTo="M -3.0,-7.0 l 6.0,0.0 c 3.8659932486,0.0 7.0,3.1340067514 7.0,7.0 l 0.0,0.0 c 0.0,3.8659932486 -3.1340067514,7.0 -7.0,7.0 l -6.0,0.0 c -3.8659932486,0.0 -7.0,-3.1340067514 -7.0,-7.0 l 0.0,0.0 c 0.0,-3.8659932486 3.1340067514,-7.0 7.0,-7.0 Z"
+ android:valueType="pathType" />
+ <objectAnimator
+ android:duration="66"
+ android:interpolator="@android:interpolator/linear"
+ android:propertyName="pathData"
+ android:valueFrom="M -3.0,-7.0 l 6.0,0.0 c 3.8659932486,0.0 7.0,3.1340067514 7.0,7.0 l 0.0,0.0 c 0.0,3.8659932486 -3.1340067514,7.0 -7.0,7.0 l -6.0,0.0 c -3.8659932486,0.0 -7.0,-3.1340067514 -7.0,-7.0 l 0.0,0.0 c 0.0,-3.8659932486 3.1340067514,-7.0 7.0,-7.0 Z"
+ android:valueTo="M 0.0,-7.0 l 0.0,0.0 c 3.8659932486,0.0 7.0,3.1340067514 7.0,7.0 l 0.0,0.0 c 0.0,3.8659932486 -3.1340067514,7.0 -7.0,7.0 l 0.0,0.0 c -3.8659932486,0.0 -7.0,-3.1340067514 -7.0,-7.0 l 0.0,0.0 c 0.0,-3.8659932486 3.1340067514,-7.0 7.0,-7.0 Z"
+ android:valueType="pathType" />
+</set>
diff --git a/packages/SystemUI/res/layout/night_mode_settings.xml b/core/res/res/color/watch_switch_thumb_color_material.xml
similarity index 67%
copy from packages/SystemUI/res/layout/night_mode_settings.xml
copy to core/res/res/color/watch_switch_thumb_color_material.xml
index 3725e78..d4796a0 100644
--- a/packages/SystemUI/res/layout/night_mode_settings.xml
+++ b/core/res/res/color/watch_switch_thumb_color_material.xml
@@ -1,12 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
@@ -14,11 +11,8 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <include layout="@layout/switch_bar" />
-
-</LinearLayout>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="?android:colorButtonNormal" android:state_enabled="false" />
+ <item android:color="?android:colorControlActivated" android:state_checked="true" />
+ <item android:color="?android:colorButtonNormal" />
+</selector>
\ No newline at end of file
diff --git a/packages/DocumentsUI/res/values/attrs.xml b/core/res/res/drawable-watch/dialog_background_material.xml
similarity index 73%
copy from packages/DocumentsUI/res/values/attrs.xml
copy to core/res/res/drawable-watch/dialog_background_material.xml
index 9e13001..de52f08 100644
--- a/packages/DocumentsUI/res/values/attrs.xml
+++ b/core/res/res/drawable-watch/dialog_background_material.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- 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.
@@ -13,8 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources>
- <declare-styleable name="DocumentsTheme">
- <attr name="colorActionMode" format="color"/>
- </declare-styleable>
-</resources>
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <solid android:color="?attr/colorBackground" />
+</shape>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_apk.xml b/core/res/res/drawable/ic_doc_apk.xml
similarity index 100%
rename from packages/DocumentsUI/res/drawable/ic_doc_apk.xml
rename to core/res/res/drawable/ic_doc_apk.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_audio.xml b/core/res/res/drawable/ic_doc_audio.xml
similarity index 100%
rename from packages/DocumentsUI/res/drawable/ic_doc_audio.xml
rename to core/res/res/drawable/ic_doc_audio.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_certificate.xml b/core/res/res/drawable/ic_doc_certificate.xml
similarity index 100%
rename from packages/DocumentsUI/res/drawable/ic_doc_certificate.xml
rename to core/res/res/drawable/ic_doc_certificate.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_codes.xml b/core/res/res/drawable/ic_doc_codes.xml
similarity index 100%
rename from packages/DocumentsUI/res/drawable/ic_doc_codes.xml
rename to core/res/res/drawable/ic_doc_codes.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_compressed.xml b/core/res/res/drawable/ic_doc_compressed.xml
similarity index 100%
rename from packages/DocumentsUI/res/drawable/ic_doc_compressed.xml
rename to core/res/res/drawable/ic_doc_compressed.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_contact.xml b/core/res/res/drawable/ic_doc_contact.xml
similarity index 100%
rename from packages/DocumentsUI/res/drawable/ic_doc_contact.xml
rename to core/res/res/drawable/ic_doc_contact.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_document.xml b/core/res/res/drawable/ic_doc_document.xml
similarity index 100%
rename from packages/DocumentsUI/res/drawable/ic_doc_document.xml
rename to core/res/res/drawable/ic_doc_document.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_event.xml b/core/res/res/drawable/ic_doc_event.xml
similarity index 100%
rename from packages/DocumentsUI/res/drawable/ic_doc_event.xml
rename to core/res/res/drawable/ic_doc_event.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_excel.xml b/core/res/res/drawable/ic_doc_excel.xml
similarity index 100%
rename from packages/DocumentsUI/res/drawable/ic_doc_excel.xml
rename to core/res/res/drawable/ic_doc_excel.xml
diff --git a/core/res/res/drawable/ic_doc_folder.xml b/core/res/res/drawable/ic_doc_folder.xml
new file mode 100644
index 0000000..dcbce01
--- /dev/null
+++ b/core/res/res/drawable/ic_doc_folder.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF737373"
+ android:pathData="M10 4H4c-1.1 0,-1.99.9,-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2,-.9 2,-2V8c0,-1.1,-.9,-2,-2,-2h-8l-2,-2z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_font.xml b/core/res/res/drawable/ic_doc_font.xml
similarity index 100%
rename from packages/DocumentsUI/res/drawable/ic_doc_font.xml
rename to core/res/res/drawable/ic_doc_font.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_generic.xml b/core/res/res/drawable/ic_doc_generic.xml
similarity index 100%
rename from packages/DocumentsUI/res/drawable/ic_doc_generic.xml
rename to core/res/res/drawable/ic_doc_generic.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_image.xml b/core/res/res/drawable/ic_doc_image.xml
similarity index 100%
rename from packages/DocumentsUI/res/drawable/ic_doc_image.xml
rename to core/res/res/drawable/ic_doc_image.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_pdf.xml b/core/res/res/drawable/ic_doc_pdf.xml
similarity index 100%
rename from packages/DocumentsUI/res/drawable/ic_doc_pdf.xml
rename to core/res/res/drawable/ic_doc_pdf.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_powerpoint.xml b/core/res/res/drawable/ic_doc_powerpoint.xml
similarity index 100%
rename from packages/DocumentsUI/res/drawable/ic_doc_powerpoint.xml
rename to core/res/res/drawable/ic_doc_powerpoint.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_presentation.xml b/core/res/res/drawable/ic_doc_presentation.xml
similarity index 100%
rename from packages/DocumentsUI/res/drawable/ic_doc_presentation.xml
rename to core/res/res/drawable/ic_doc_presentation.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_spreadsheet.xml b/core/res/res/drawable/ic_doc_spreadsheet.xml
similarity index 100%
rename from packages/DocumentsUI/res/drawable/ic_doc_spreadsheet.xml
rename to core/res/res/drawable/ic_doc_spreadsheet.xml
diff --git a/core/res/res/drawable/ic_doc_text.xml b/core/res/res/drawable/ic_doc_text.xml
new file mode 100644
index 0000000..7fc04e8
--- /dev/null
+++ b/core/res/res/drawable/ic_doc_text.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF737373"
+ android:pathData="M14 2H6c-1.1 0,-1.99.9,-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2,-.9 2,-2V8l-6,-6zm2 16H8v-2h8v2zm0,-4H8v-2h8v2zm-3,-5V3.5L18.5 9H13z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_video.xml b/core/res/res/drawable/ic_doc_video.xml
similarity index 100%
rename from packages/DocumentsUI/res/drawable/ic_doc_video.xml
rename to core/res/res/drawable/ic_doc_video.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_word.xml b/core/res/res/drawable/ic_doc_word.xml
similarity index 100%
rename from packages/DocumentsUI/res/drawable/ic_doc_word.xml
rename to core/res/res/drawable/ic_doc_word.xml
diff --git a/core/res/res/drawable/ic_lock_power_off.xml b/core/res/res/drawable/ic_lock_power_off.xml
index 718f17e..babd1be 100644
--- a/core/res/res/drawable/ic_lock_power_off.xml
+++ b/core/res/res/drawable/ic_lock_power_off.xml
@@ -1,19 +1,25 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ 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
+ 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
+ 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.
+ 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.
-->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_lock_power_off_alpha"
- android:tint="?attr/colorControlNormal" />
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13.0,3.0l-2.0,0.0l0.0,10.0l2.0,0.0L13.0,3.0zm4.83,2.17l-1.42,1.42C17.99,7.86 19.0,9.81 19.0,12.0c0.0,3.87 -3.13,7.0 -7.0,7.0s-7.0,-3.13 -7.0,-7.0c0.0,-2.19 1.01,-4.14 2.58,-5.42L6.17,5.17C4.23,6.82 3.0,9.26 3.0,12.0c0.0,4.97 4.03,9.0 9.0,9.0s9.0,-4.03 9.0,-9.0c0.0,-2.74 -1.23,-5.18 -3.17,-6.83z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_night_mode_disabled.xml b/core/res/res/drawable/ic_restart.xml
similarity index 60%
copy from packages/SystemUI/res/drawable/ic_night_mode_disabled.xml
copy to core/res/res/drawable/ic_restart.xml
index 010815a..47ac483 100644
--- a/packages/SystemUI/res/drawable/ic_night_mode_disabled.xml
+++ b/core/res/res/drawable/ic_restart.xml
@@ -16,9 +16,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.0dp"
android:height="24.0dp"
+ android:viewportHeight="24.0"
android:viewportWidth="24.0"
- android:viewportHeight="24.0">
+ android:tint="?attr/colorControlNormal">
<path
- android:fillColor="#4DFFFFFF"
- android:pathData="M20.71,5.63l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0.0l-3.12,3.12 -1.93,-1.91 -1.41,1.41 1.42,1.42L3.0,16.25L3.0,21.0l4.75,0.0l8.92,-8.92 1.42,1.42 1.41,-1.41 -1.92,-1.92 3.12,-3.12c0.4,0.0 0.4,-1.0 0.01,-1.42zM6.92,19.0L5.0,17.08l8.06,-8.06 1.92,1.92L6.92,19.0z"/>
+ android:fillColor="#FF000000"
+ android:pathData="M12.0,4.0L12.0,1.0L8.0,5.0l4.0,4.0L12.0,6.0c3.9,0.0 7.0,3.1 7.0,7.0c0.0,3.9 -3.1,7.0 -7.0,7.0l0.0,2.0c5.0,0.0 9.0,-4.0 9.0,-9.0C21.0,8.0 17.0,4.0 12.0,4.0z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5.0,12.9C5.0,11.0 5.8,9.2 7.2,7.9L5.8,6.4C4.0,8.1 3.0,10.5 3.0,12.9c0.0,4.0 2.7,7.6 6.5,8.7l0.5,-1.9C7.1,18.8 5.0,16.1 5.0,12.9z"/>
</vector>
diff --git a/core/res/res/drawable/watch_switch_thumb_material.xml b/core/res/res/drawable/watch_switch_thumb_material.xml
new file mode 100644
index 0000000..3463a4f
--- /dev/null
+++ b/core/res/res/drawable/watch_switch_thumb_material.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="40dp"
+ android:viewportHeight="40"
+ android:viewportWidth="20">
+ <group
+ android:translateX="10"
+ android:translateY="20">
+ <path
+ android:name="thumb_path"
+ android:fillColor="@color/white"
+ android:pathData="M 0.0,-7.0 l 0.0,0.0 c 3.8659932486,0.0 7.0,3.1340067514 7.0,7.0 l 0.0,0.0 c 0.0,3.8659932486 -3.1340067514,7.0 -7.0,7.0 l 0.0,0.0 c -3.8659932486,0.0 -7.0,-3.1340067514 -7.0,-7.0 l 0.0,0.0 c 0.0,-3.8659932486 3.1340067514,-7.0 7.0,-7.0 Z" />
+ </group>
+</vector>
diff --git a/core/res/res/drawable/watch_switch_thumb_material_anim.xml b/core/res/res/drawable/watch_switch_thumb_material_anim.xml
new file mode 100644
index 0000000..686fb97
--- /dev/null
+++ b/core/res/res/drawable/watch_switch_thumb_material_anim.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<animated-selector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:constantSize="true">
+ <item
+ android:id="@+id/off"
+ android:drawable="@drawable/watch_switch_thumb_material"
+ android:state_enabled="false" />
+ <item
+ android:id="@+id/on"
+ android:drawable="@drawable/watch_switch_thumb_material"
+ android:state_checked="true" />
+ <item
+ android:id="@+id/off"
+ android:drawable="@drawable/watch_switch_thumb_material" />
+ <transition
+ android:drawable="@drawable/watch_switch_thumb_to_on_anim_mtrl"
+ android:fromId="@id/off"
+ android:toId="@id/on" />
+ <transition
+ android:drawable="@drawable/watch_switch_thumb_to_off_anim_mtrl"
+ android:fromId="@id/on"
+ android:toId="@id/off" />
+</animated-selector>
diff --git a/packages/SystemUI/res/layout/night_mode_settings.xml b/core/res/res/drawable/watch_switch_thumb_to_off_anim_mtrl.xml
similarity index 70%
copy from packages/SystemUI/res/layout/night_mode_settings.xml
copy to core/res/res/drawable/watch_switch_thumb_to_off_anim_mtrl.xml
index 3725e78..2c6ba2f 100644
--- a/packages/SystemUI/res/layout/night_mode_settings.xml
+++ b/core/res/res/drawable/watch_switch_thumb_to_off_anim_mtrl.xml
@@ -1,12 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
@@ -14,11 +11,9 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <include layout="@layout/switch_bar" />
-
-</LinearLayout>
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/watch_switch_thumb_material">
+ <target
+ android:name="thumb_path"
+ android:animation="@anim/watch_switch_thumb_to_off_animation" />
+</animated-vector>
diff --git a/packages/SystemUI/res/layout/night_mode_settings.xml b/core/res/res/drawable/watch_switch_thumb_to_on_anim_mtrl.xml
similarity index 70%
copy from packages/SystemUI/res/layout/night_mode_settings.xml
copy to core/res/res/drawable/watch_switch_thumb_to_on_anim_mtrl.xml
index 3725e78..9f92361 100644
--- a/packages/SystemUI/res/layout/night_mode_settings.xml
+++ b/core/res/res/drawable/watch_switch_thumb_to_on_anim_mtrl.xml
@@ -1,12 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
@@ -14,11 +11,9 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <include layout="@layout/switch_bar" />
-
-</LinearLayout>
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/watch_switch_thumb_material">
+ <target
+ android:name="thumb_path"
+ android:animation="@anim/watch_switch_thumb_to_on_animation" />
+</animated-vector>
diff --git a/packages/SystemUI/res/layout/night_mode_settings.xml b/core/res/res/drawable/watch_switch_track_material.xml
similarity index 63%
copy from packages/SystemUI/res/layout/night_mode_settings.xml
copy to core/res/res/drawable/watch_switch_track_material.xml
index 3725e78..00cdadb 100644
--- a/packages/SystemUI/res/layout/night_mode_settings.xml
+++ b/core/res/res/drawable/watch_switch_track_material.xml
@@ -1,12 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
@@ -14,11 +11,13 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <include layout="@layout/switch_bar" />
-
-</LinearLayout>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:gravity="center_vertical|center_horizontal">
+ <shape android:shape="oval">
+ <solid android:color="@android:color/white" />
+ <size
+ android:width="40dp"
+ android:height="40dp" />
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/core/res/res/layout-notround-watch/alert_dialog_header_micro.xml b/core/res/res/layout-notround-watch/alert_dialog_header_micro.xml
new file mode 100644
index 0000000..fc840d9
--- /dev/null
+++ b/core/res/res/layout-notround-watch/alert_dialog_header_micro.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="top|center_horizontal"
+ android:minHeight="@dimen/alert_dialog_title_height">
+ <ImageView android:id="@+id/icon"
+ android:maxHeight="24dp"
+ android:maxWidth="24dp"
+ android:layout_marginTop="8dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginBottom="8dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@null" />
+ <com.android.internal.widget.DialogTitle android:id="@+id/alertTitle"
+ style="?android:attr/windowTitleStyle"
+ android:ellipsize="end"
+ android:layout_marginStart="8dp"
+ android:layout_marginBottom="8dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="viewStart" />
+</LinearLayout>
diff --git a/core/res/res/layout-round-watch/alert_dialog_header_micro.xml b/core/res/res/layout-round-watch/alert_dialog_header_micro.xml
new file mode 100644
index 0000000..6f7ae02
--- /dev/null
+++ b/core/res/res/layout-round-watch/alert_dialog_header_micro.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="top|center_horizontal"
+ android:minHeight="@dimen/alert_dialog_title_height">
+ <ImageView android:id="@+id/icon"
+ android:maxHeight="24dp"
+ android:maxWidth="24dp"
+ android:layout_marginTop="12dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@null" />
+ <com.android.internal.widget.DialogTitle android:id="@+id/alertTitle"
+ style="?android:attr/windowTitleStyle"
+ android:ellipsize="end"
+ android:layout_marginTop="36dp"
+ android:layout_marginBottom="4dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="center" />
+</FrameLayout>
diff --git a/core/res/res/layout-watch/alert_dialog_material.xml b/core/res/res/layout-watch/alert_dialog_material.xml
new file mode 100644
index 0000000..ce8e20a
--- /dev/null
+++ b/core/res/res/layout-watch/alert_dialog_material.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<com.android.internal.widget.WatchListDecorLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/parentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ScrollView
+ android:id="@+id/scrollView"
+ android:fillViewport="true"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <!-- Top Panel -->
+ <FrameLayout
+ android:paddingLeft="?dialogPreferredPadding"
+ android:paddingRight="?dialogPreferredPadding"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/topPanel"
+ android:minHeight="@dimen/dialog_list_padding_top_no_title">
+ <include android:id="@+id/title_template"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ layout="@layout/alert_dialog_header_micro"/>
+ </FrameLayout>
+
+ <!-- Content Panel -->
+ <FrameLayout android:id="@+id/contentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false">
+ <TextView android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.Material.Body1"
+ android:paddingStart="?dialogPreferredPadding"
+ android:paddingEnd="?dialogPreferredPadding"
+ android:paddingTop="8dip"
+ android:paddingBottom="8dip"/>
+ </FrameLayout>
+
+ <!-- Custom Panel, to replace content panel if needed -->
+ <FrameLayout android:id="@+id/customPanel"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:minHeight="64dp">
+ <FrameLayout android:id="@+android:id/custom"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </FrameLayout>
+
+ <!-- Button Panel -->
+ <FrameLayout
+ android:id="@+id/buttonPanel"
+ android:minHeight="@dimen/dialog_list_padding_bottom_no_buttons"
+ android:layout_weight="1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
+ android:paddingBottom="?dialogPreferredPadding"
+ style="?android:attr/buttonBarStyle"
+ android:measureWithLargestChild="true">
+ <Button android:id="@+id/button1"
+ android:layout_gravity="start"
+ android:layout_weight="1"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ <Button android:id="@+id/button3"
+ android:layout_gravity="start"
+ android:layout_weight="1"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ <Button android:id="@+id/button2"
+ android:layout_gravity="start"
+ android:layout_weight="1"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+ </FrameLayout>
+ </LinearLayout>
+ </ScrollView>
+</com.android.internal.widget.WatchListDecorLayout>
diff --git a/core/res/res/layout-watch/input_method_extract_view.xml b/core/res/res/layout-watch/input_method_extract_view.xml
index e3cd2ce..038b766 100644
--- a/core/res/res/layout-watch/input_method_extract_view.xml
+++ b/core/res/res/layout-watch/input_method_extract_view.xml
@@ -31,10 +31,10 @@
android:inputType="text"
android:layout_weight="1"
android:fontFamily="sans-serif-condensed-light"
- android:textColor="@color/primary_text_default_material_dark"
+ android:textColor="@color/primary_text_material_dark"
+ android:textColorHint="@color/secondary_text_material_dark"
android:textColorHighlight="@color/accent_material_dark"
android:textSize="18dp"
- android:cursorVisible="false"
android:gravity="bottom|right"
/>
diff --git a/core/res/res/layout/number_picker_with_selector_wheel_micro.xml b/core/res/res/layout-watch/number_picker_material.xml
similarity index 100%
rename from core/res/res/layout/number_picker_with_selector_wheel_micro.xml
rename to core/res/res/layout-watch/number_picker_material.xml
diff --git a/core/res/res/layout-watch/preference_list_fragment_material.xml b/core/res/res/layout-watch/preference_list_fragment_material.xml
new file mode 100644
index 0000000..ae8f203
--- /dev/null
+++ b/core/res/res/layout-watch/preference_list_fragment_material.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:background="@android:color/transparent"
+ android:layout_removeBorders="true">
+
+ <FrameLayout
+ android:id="@android:id/list_container"
+ android:layout_width="match_parent"
+ android:layout_height="0px"
+ android:layout_weight="1">
+ <com.android.internal.widget.WatchHeaderListView
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ style="?attr/preferenceFragmentListStyle"
+ android:scrollbarStyle="@integer/preference_fragment_scrollbarStyle"
+ android:clipToPadding="false"
+ android:drawSelectorOnTop="false"
+ android:cacheColorHint="@android:color/transparent"
+ android:scrollbarAlwaysDrawVerticalTrack="true">
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="@dimen/dialog_padding_material"
+ android:paddingEnd="@dimen/dialog_padding_material"
+ android:paddingBottom="8dp"
+ android:textAppearance="@style/TextAppearance.Material.Title"
+ android:gravity="center" />
+ </com.android.internal.widget.WatchHeaderListView>
+ </FrameLayout>
+
+ <TextView android:id="@android:id/empty"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="@dimen/preference_fragment_padding_side"
+ android:gravity="center"
+ android:visibility="gone" />
+
+ <RelativeLayout android:id="@+id/button_bar"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_weight="0"
+ android:visibility="gone">
+
+ <Button android:id="@+id/back_button"
+ android:layout_width="150dip"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dip"
+ android:layout_alignParentStart="true"
+ android:text="@string/back_button_label"
+ />
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true">
+
+ <Button android:id="@+id/skip_button"
+ android:layout_width="150dip"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dip"
+ android:text="@string/skip_button_label"
+ android:visibility="gone"
+ />
+
+ <Button android:id="@+id/next_button"
+ android:layout_width="150dip"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dip"
+ android:text="@string/next_button_label"
+ />
+ </LinearLayout>
+ </RelativeLayout>
+</LinearLayout>
diff --git a/core/res/res/layout-watch/preference_material.xml b/core/res/res/layout-watch/preference_material.xml
new file mode 100644
index 0000000..ad217db
--- /dev/null
+++ b/core/res/res/layout-watch/preference_material.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Layout for a Preference in a PreferenceActivity. The
+ Preference is able to place a specific widget for its particular
+ type in the "widget_frame" layout. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:paddingStart="?attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?attr/listPreferredItemPaddingEnd"
+ android:background="?attr/activatedBackgroundIndicator"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:gravity="center"
+ android:orientation="horizontal"
+ android:layout_marginEnd="8dp"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp">
+ <com.android.internal.widget.PreferenceImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxWidth="40dp"
+ android:maxHeight="40dp" />
+ </LinearLayout>
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout android:id="@+id/widget_frame"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:gravity="center"
+ android:orientation="horizontal"
+ android:layout_marginEnd="8dp"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp" />
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp">
+
+ <TextView android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="3"
+ android:textAppearance="?attr/textAppearanceListItem"
+ android:ellipsize="end" />
+
+ <TextView android:id="@+id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/title"
+ android:layout_alignStart="@id/title"
+ android:textAppearance="?attr/textAppearanceListItemSecondary"
+ android:textColor="?attr/textColorSecondary"
+ android:maxLines="10" />
+
+ </RelativeLayout>
+</LinearLayout>
diff --git a/core/res/res/layout-watch/preference_widget_switch.xml b/core/res/res/layout-watch/preference_widget_switch.xml
new file mode 100644
index 0000000..37d0c6b
--- /dev/null
+++ b/core/res/res/layout-watch/preference_widget_switch.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Layout for a SwitchPreference -->
+<Switch xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+android:id/switch_widget"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:switchMinWidth="40dp"
+ android:layout_gravity="center"
+ android:thumb="@drawable/watch_switch_thumb_material_anim"
+ android:thumbTint="@color/watch_switch_thumb_color_material"
+ android:track="@drawable/watch_switch_track_material"
+ android:trackTint="?android:colorPrimary"
+ android:focusable="false"
+ android:clickable="false"
+ android:background="@null" />
diff --git a/core/res/res/layout/alert_dialog_micro.xml b/core/res/res/layout/alert_dialog_micro.xml
deleted file mode 100644
index abdbd16..0000000
--- a/core/res/res/layout/alert_dialog_micro.xml
+++ /dev/null
@@ -1,140 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/parentPanel"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@android:color/white"
- android:layout_gravity="center"
- android:orientation="vertical">
-
- <LinearLayout android:id="@+id/topPanel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
- <View android:id="@+id/titleDividerTop"
- android:layout_width="match_parent"
- android:layout_height="2dip"
- android:visibility="gone"
- android:background="@android:color/holo_blue_light" />
- <LinearLayout android:id="@+id/title_template"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:gravity="center_vertical|start"
- android:minHeight="@dimen/alert_dialog_title_height"
- android:layout_marginStart="16dip"
- android:layout_marginEnd="16dip">
- <ImageView android:id="@+id/icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingEnd="8dip"
- android:src="@null" />
- <com.android.internal.widget.DialogTitle android:id="@+id/alertTitle"
- style="?android:attr/windowTitleStyle"
- android:singleLine="true"
- android:ellipsize="end"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAlignment="viewStart" />
- </LinearLayout>
- <View android:id="@+id/titleDivider"
- android:layout_width="match_parent"
- android:layout_height="2dip"
- android:visibility="gone"
- android:background="@android:color/holo_blue_light" />
- <!-- If the client uses a customTitle, it will be added here. -->
- </LinearLayout>
-
- <LinearLayout android:id="@+id/contentPanel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical"
- android:minHeight="64dp">
- <ScrollView android:id="@+id/scrollView"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipToPadding="false">
- <TextView android:id="@+id/message"
- style="?android:attr/textAppearanceMedium"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="16dip"
- android:paddingEnd="16dip"
- android:paddingTop="8dip"
- android:paddingBottom="8dip"/>
- </ScrollView>
- </LinearLayout>
-
- <FrameLayout android:id="@+id/customPanel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:minHeight="64dp">
- <FrameLayout android:id="@+android:id/custom"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- </FrameLayout>
-
- <LinearLayout android:id="@+id/buttonPanel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- android:orientation="vertical"
- android:divider="?android:attr/dividerHorizontal"
- android:showDividers="beginning"
- android:dividerPadding="0dip">
- <LinearLayout
- style="?android:attr/buttonBarStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layoutDirection="locale"
- android:measureWithLargestChild="true">
- <Button android:id="@+id/button2"
- android:layout_width="wrap_content"
- android:layout_gravity="start"
- android:layout_weight="1"
- android:maxLines="2"
- style="?android:attr/buttonBarButtonStyle"
- android:textSize="14sp"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- android:layout_height="wrap_content" />
- <Button android:id="@+id/button3"
- android:layout_width="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_weight="1"
- android:maxLines="2"
- style="?android:attr/buttonBarButtonStyle"
- android:textSize="14sp"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- android:layout_height="wrap_content" />
- <Button android:id="@+id/button1"
- android:layout_width="wrap_content"
- android:layout_gravity="end"
- android:layout_weight="1"
- android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- style="?android:attr/buttonBarButtonStyle"
- android:textSize="14sp"
- android:layout_height="wrap_content" />
- </LinearLayout>
- </LinearLayout>
-</LinearLayout>
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index d12c8ba..d8dd447 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -41,7 +41,7 @@
android:visibility="gone"
style="?attr/borderlessButtonStyle"
android:textAppearance="?attr/textAppearanceButton"
- android:textColor="@color/material_deep_teal_500"
+ android:textColor="?attr/colorAccent"
android:gravity="center_vertical"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
diff --git a/core/res/res/layout/date_picker_header_material.xml b/core/res/res/layout/date_picker_header_material.xml
index a0e2b1d..755317e 100644
--- a/core/res/res/layout/date_picker_header_material.xml
+++ b/core/res/res/layout/date_picker_header_material.xml
@@ -54,7 +54,7 @@
android:textAppearance="@style/TextAppearance.Material.DatePicker.DateLabel"
android:includeFontPadding="false"
android:gravity="start"
- android:maxLines="2"
+ android:maxLines="@integer/date_picker_header_max_lines_material"
android:ellipsize="none" />
</LinearLayout>
</LinearLayout>
diff --git a/core/res/res/layout/immersive_mode_cling.xml b/core/res/res/layout/immersive_mode_cling.xml
index 28fbea5..b08b0f4 100644
--- a/core/res/res/layout/immersive_mode_cling.xml
+++ b/core/res/res/layout/immersive_mode_cling.xml
@@ -16,7 +16,7 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="#ff009688"
+ android:background="?android:attr/colorAccent"
android:gravity="center_vertical"
android:paddingBottom="24dp">
@@ -47,7 +47,7 @@
android:paddingTop="8dp"
android:scaleType="center"
android:src="@drawable/ic_expand_more_48dp"
- android:tint="#ff009688"/>
+ android:tint="?android:attr/colorAccent"/>
</FrameLayout>
<TextView
diff --git a/core/res/res/layout/notification_material_action_emphasized.xml b/core/res/res/layout/notification_material_action_emphasized.xml
new file mode 100644
index 0000000..1e364cc
--- /dev/null
+++ b/core/res/res/layout/notification_material_action_emphasized.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/button_holder"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:background="#ff000000">
+ <Button
+ style="@android:style/Widget.Material.Light.Button.Borderless.Small"
+ android:id="@+id/action0"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:textColor="@color/notification_default_color"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:background="@drawable/notification_material_action_background"
+ />
+</FrameLayout>
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 38f671c2..1f71a18 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -26,7 +26,7 @@
android:paddingBottom="16dp"
android:paddingStart="@dimen/notification_content_margin_start"
android:paddingEnd="16dp">
- <ImageView
+ <com.android.internal.widget.CachingIconView
android:id="@+id/icon"
android:layout_width="18dp"
android:layout_height="18dp"
diff --git a/core/res/res/layout/preference_list_fragment.xml b/core/res/res/layout/preference_list_fragment.xml
index fc53a1a..c43975e 100644
--- a/core/res/res/layout/preference_list_fragment.xml
+++ b/core/res/res/layout/preference_list_fragment.xml
@@ -33,8 +33,6 @@
style="?attr/preferenceFragmentListStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingTop="0dip"
- android:paddingBottom="@dimen/preference_fragment_padding_bottom"
android:scrollbarStyle="@integer/preference_fragment_scrollbarStyle"
android:clipToPadding="false"
android:drawSelectorOnTop="false"
diff --git a/core/res/res/layout/preference_list_fragment_material.xml b/core/res/res/layout/preference_list_fragment_material.xml
index e411c0e..db2fe7d 100644
--- a/core/res/res/layout/preference_list_fragment_material.xml
+++ b/core/res/res/layout/preference_list_fragment_material.xml
@@ -32,8 +32,6 @@
<ListView android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingTop="0dip"
- android:paddingBottom="@dimen/preference_fragment_padding_bottom"
style="?attr/preferenceFragmentListStyle"
android:scrollbarStyle="@integer/preference_fragment_scrollbarStyle"
android:clipToPadding="false"
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index ae94503..c4e8e9c 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -42,7 +42,7 @@
android:visibility="gone"
style="?attr/borderlessButtonStyle"
android:textAppearance="?attr/textAppearanceButton"
- android:textColor="@color/material_deep_teal_500"
+ android:textColor="?attr/colorAccent"
android:gravity="center_vertical"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
diff --git a/core/res/res/values-af-watch/styles_material.xml b/core/res/res/values-af-watch/styles_material.xml
new file mode 100644
index 0000000..80a5fa6
--- /dev/null
+++ b/core/res/res/values-af-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"kandidate"</font></string>
+</resources>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 9c16f7e..bd92ab6 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Reg!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Probeer weer"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Probeer weer"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Ontsluit vir alle kenmerke en data"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maksimum gesigontsluit-pogings oorskry"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Geen SIM-kaart nie"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Geen SIM-kaart in tablet nie."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android gradeer tans op..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android begin tans …"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimeer tans berging."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android gradeer tans op"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Voltooi tans Android-opdatering …"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Sommige programme sal dalk nie behoorlik werk voordat die opgradering voltooi is nie"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> gradeer tans op …"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optimeer program <xliff:g id="NUMBER_0">%1$d</xliff:g> van <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Berei tans <xliff:g id="APPNAME">%1$s</xliff:g> voor."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Begin programme."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Ontspeld"</string>
<string name="app_info" msgid="6856026610594615344">"Programinligting"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Stel toestel terug?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Tik om toestel terug te stel"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Begin tans demonstrasie …"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Stel toestel tans terug …"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Stel toestel terug?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Jy sal enige veranderinge verloor en die demonstrasie sal oor <xliff:g id="TIMEOUT">%1$s</xliff:g> sekondes weer begin …"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Kanselleer"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Stel nou terug"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Doen \'n fabriekterugstelling om hierdie toestel sonder beperkinge te gebruik"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Raak om meer te wete te kom."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Het <xliff:g id="LABEL">%1$s</xliff:g> gedeaktiveer"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Konferensie-oproep"</string>
</resources>
diff --git a/core/res/res/values-am-watch/styles_material.xml b/core/res/res/values-am-watch/styles_material.xml
new file mode 100644
index 0000000..5ec383a8
--- /dev/null
+++ b/core/res/res/values-am-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"እጩዎች"</font></string>
+</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index a3c18ac..98fcebb 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"ትክክል!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"እንደገና ሞክር"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"እንደገና ሞክር"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"ለሁሉም ባህሪያት እና ውሂብ ያስከፍቱ"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"የመጨረሻውን የገጽ ክፈት ሙከራዎችን አልፏል"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"ምንም ሲም ካርድ የለም"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"በጡባዊ ውስጥ ምንም SIM ካርድ የለም።"</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android እያሻሻለ ነው..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android በመጀመር ላይ ነው…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"ማከማቻን በማመቻቸት ላይ።"</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android በማላቅ ላይ ነው"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"የAndroid ዝማኔን በመጨረስ ላይ…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"አንዳንድ መተግበሪያዎች ማላቁ እስኪጠናቀቅ ድረስ በአግባቡ ላይሰሩ ይችላሉ"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> በማላቅ ላይ…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"መተግበሪያዎች በአግባቡ በመጠቀም ላይ <xliff:g id="NUMBER_0">%1$d</xliff:g> ከ <xliff:g id="NUMBER_1">%2$d</xliff:g> ፡፡"</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g>ን ማዘጋጀት።"</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"መተግበሪያዎችን በማስጀመር ላይ፡፡"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"ንቀል"</string>
<string name="app_info" msgid="6856026610594615344">"የመተግበሪያ መረጃ"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"መሣሪያ ዳግም ይጀመር?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"መሣሪያን ዳግም ለማስጀመር መታ ያድርጉ"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"ማሳያን በማስጀመር ላይ…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"መሣሪያን ዳግም በማስጀመር ላይ…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"መሣሪያ ዳግም ይጀመር?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"ማንኛቸውም ለውጦች ይጠፋሉ፣ እና ማሳያው በ<xliff:g id="TIMEOUT">%1$s</xliff:g> ሰከንዶች ውስጥ እንደገና ይጀምራል…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"ይቅር"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"አሁን ዳግም አስጀምር"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"ይህን መሣሪያ ያለምንም ገደብ ለመጠቀም የፋብሪካ ዳግም ያስጀምሩ"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"የበለጠ ለመረዳት ይንኩ።"</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> ተሰናክሏል"</string>
+ <string name="conference_call" msgid="3751093130790472426">"የስብሰባ ጥሪ"</string>
</resources>
diff --git a/core/res/res/values-ar-watch/styles_material.xml b/core/res/res/values-ar-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-ar-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 2db0e15..32df247 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -690,6 +690,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"صحيح!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"أعد المحاولة"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"أعد المحاولة"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"إلغاء قفل جميع الميزات والبيانات"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"تم تجاوز الحد الأقصى لعدد محاولات تأمين الجهاز بالوجه"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"ليست هناك شريحة SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"ليس هناك شريحة SIM في الجهاز اللوحي."</string>
@@ -1114,8 +1115,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"جارٍ ترقية Android..."</string>
<string name="android_start_title" msgid="8418054686415318207">"جارٍ تشغيل Android…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"جارٍ تحسين السعة التخزينية."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"جارٍ ترقية Android"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"جارٍ إتمام تحديث Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"قد لا تعمل بعض التطبيقات بشكل مناسب إلا بعد انتهاء الترقية"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"جارٍ ترقية <xliff:g id="APPLICATION">%1$s</xliff:g>…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"جارٍ تحسين التطبيق <xliff:g id="NUMBER_0">%1$d</xliff:g> من <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"جارٍ تحضير <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"بدء التطبيقات."</string>
@@ -1795,7 +1797,16 @@
<string name="unpin_target" msgid="3556545602439143442">"إزالة تثبيت"</string>
<string name="app_info" msgid="6856026610594615344">"معلومات عن التطبيق"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"هل تريد إعادة تعيين الجهاز؟"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"انقر لإعادة تعيين الجهاز"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"جارٍ بدء العرض التوضيحي…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"جارٍ إعادة تعيين الجهاز…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"هل تريد إعادة تعيين الجهاز؟"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"ستفقد أي تغييرات وسيبدأ العرض التوضيحي مرة أخرى خلال <xliff:g id="TIMEOUT">%1$s</xliff:g> من الثواني…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"إلغاء"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"إعادة التعيين الآن"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"يمكنك إعادة تعيين بيانات المصنع لاستخدام هذا الجهاز بدون قيود"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"المس للتعرف على مزيد من المعلومات."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"تم تعطيل <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"مكالمة جماعية"</string>
</resources>
diff --git a/core/res/res/values-az-rAZ-watch/styles_material.xml b/core/res/res/values-az-rAZ-watch/styles_material.xml
new file mode 100644
index 0000000..b621266
--- /dev/null
+++ b/core/res/res/values-az-rAZ-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"namizədlər"</font></string>
+</resources>
diff --git a/core/res/res/values-az-rAZ/strings.xml b/core/res/res/values-az-rAZ/strings.xml
index 7f55ba1..b1ed7dd 100644
--- a/core/res/res/values-az-rAZ/strings.xml
+++ b/core/res/res/values-az-rAZ/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Düzdür!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Bir də cəhd edin"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Bir daha cəhd et"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Bütün funksiyalar və data üçün kiliddən çıxarın"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Sifət kilidi cəhdləriniz bitdi"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"SIM kart yoxdur."</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Planşetdə SIM kart yoxdur."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android təkmilləşdirilir..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android işə başlayır..."</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Yaddaş optimallaşdırılır."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android təkmilləşdirilir"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android güncəlləməsi tamamlanır..."</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Güncəllənmə tamamlanana kimi bəzi tətbiqlər düzgün işləməyə bilər"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> təkmilləşdirilir…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> əddədən <xliff:g id="NUMBER_0">%1$d</xliff:g> tətbiq optimallaşır."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> proqramının hazırlanması."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Tətbiqlər başladılır."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Çıxarın"</string>
<string name="app_info" msgid="6856026610594615344">"Tətbiq məlumatı"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Cihaz sıfırlansın?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Cihazı sıfırlamaq üçün tıklayın"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Demo başlayır…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Cihaz sıfırlanır…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Cihaz sıfırlansın?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Hər hansı dəyişikliyi itirəcəksiniz və demo <xliff:g id="TIMEOUT">%1$s</xliff:g> saniyəyə yenidən başlayacaq…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Ləğv edin"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"İndi sıfırlayın"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Bu cihazı məhdudiyyətsiz istifadə etmək üçün zavod sıfırlaması edin"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Daha çox məlumat üçün toxunun."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> deaktiv edildi"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Konfrans Zəngi"</string>
</resources>
diff --git a/core/res/res/values-b+sr+Latn-watch/styles_material.xml b/core/res/res/values-b+sr+Latn-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-b+sr+Latn-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index ec8f5f3..ad74e2e 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -681,6 +681,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Tačno!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Pokušajte ponovo"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Pokušajte ponovo"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Otključaj za sve funkcije i podatke"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Premašen je najveći dozvoljeni broj pokušaja Otključavanja licem"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Nema SIM kartice"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"U tabletu nema SIM kartice."</string>
@@ -1045,8 +1046,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android se nadograđuje…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android se pokreće…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Memorija se optimizuje."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android se nadograđuje…"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Dovršavamo ažuriranje Android-a…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Neke aplikacije možda neće ispravno funkcionisati dok se nadogradnja ne dovrši"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> se nadograđuje…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optimizovanje aplikacije <xliff:g id="NUMBER_0">%1$d</xliff:g> od <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Priprema se <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Pokretanje aplikacija."</string>
@@ -1687,7 +1689,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Otkači"</string>
<string name="app_info" msgid="6856026610594615344">"Informacije o aplikaciji"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Želite li da resetujete uređaj?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Dodirnite da biste resetovali uređaj"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Pokrećemo demonstraciju..."</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Resetujemo uređaj..."</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Želite li da resetujete uređaj?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Izgubićete sve promene i demonstracija će ponovo početi za <xliff:g id="TIMEOUT">%1$s</xliff:g> sek…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Otkaži"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Resetuj"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Resetujte uređaj na fabrička podešavanja da biste ga koristili bez ograničenja"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Dodirnite da biste saznali više."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Vidžet <xliff:g id="LABEL">%1$s</xliff:g> je onemogućen"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Konferencijski poziv"</string>
</resources>
diff --git a/core/res/res/values-be-rBY-watch/styles_material.xml b/core/res/res/values-be-rBY-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-be-rBY-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-be-rBY/strings.xml b/core/res/res/values-be-rBY/strings.xml
index 995e0a2..237820c 100644
--- a/core/res/res/values-be-rBY/strings.xml
+++ b/core/res/res/values-be-rBY/strings.xml
@@ -684,6 +684,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Правільна!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Паспрабуйце яшчэ раз"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Паўтарыце спробу"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Разблакіраваць для ўсіх функцый і даных"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Перавышана максімальная колькасць спроб разблакоўкі праз Фэйскантроль"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Няма SIM-карты"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Няма SIM-карты ў планшэце."</string>
@@ -1068,8 +1069,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Абнаўленне Android..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android запускаецца..."</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Аптымізацыя сховішча."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Абнаўленне Android"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Абнаўленне Android завяршаецца…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Пэўныя праграмы могуць не працаваць належным чынам, пакуль не скончыцца абнаўленне"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> абнаўляецца…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Аптымізацыя прыкладання <xliff:g id="NUMBER_0">%1$d</xliff:g> з <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Падрыхтоўка <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Запуск прыкладанняў."</string>
@@ -1723,7 +1725,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Адмацаваць"</string>
<string name="app_info" msgid="6856026610594615344">"Інфармацыя пра праграму"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Скінуць налады прылады?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Дакраніцеся, каб скінуць налады прылады"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Ідзе запуск дэманстрацыі…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Ідзе скід налад прылады…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Скінуць налады прылады?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Усе змены будуць страчаны, і дэманстрацыя пачнецца зноў праз <xliff:g id="TIMEOUT">%1$s</xliff:g> с…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Скасаваць"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Выканаць скід"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Выканайце скід да заводскіх налад, каб выкарыстоўваць гэту прыладу без абмежаванняў"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Краніце, каб даведацца больш."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Адключаны <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Канферэнц-выклік"</string>
</resources>
diff --git a/core/res/res/values-bg-watch/styles_material.xml b/core/res/res/values-bg-watch/styles_material.xml
new file mode 100644
index 0000000..89c3366
--- /dev/null
+++ b/core/res/res/values-bg-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"кандидати"</font></string>
+</resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index a70d9dc..098a1a56 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Правилно!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Опитайте отново"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Опитайте отново"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Отключете за достъп до всички функции и данни"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Максималният брой опити за отключване с лице е надвишен"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Няма SIM карта"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"В таблета няма SIM карта."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android се надстройва..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android се стартира…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Хранилището се оптимизира."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android се надстройва"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Актуализацията на Android приключва…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Някои приложения може да не работят правилно, докато надстройването не завърши"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> се надстройва…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Оптимизира се приложение <xliff:g id="NUMBER_0">%1$d</xliff:g> от <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> се подготвя."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Приложенията се стартират."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Освобождаване"</string>
<string name="app_info" msgid="6856026610594615344">"Информация за приложението"</string>
<string name="negative_duration" msgid="5688706061127375131">"-<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Да се нулира ли устройството?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Докоснете, за да нулирате устройството"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Демонстрацията се стартира…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Устройството се нулира…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Да се нулира ли устройството?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Ще загубите всички промени и демонстрацията ще започне отново след <xliff:g id="TIMEOUT">%1$s</xliff:g> секунди…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Отказ"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Нулиране сега"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Възстановете фабричните настройки на това устройство, за да го използвате без ограничения"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Докоснете, за да научите повече."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g>: Деактивирано"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Конферентно обаждане"</string>
</resources>
diff --git a/core/res/res/values-bn-rBD-watch/styles_material.xml b/core/res/res/values-bn-rBD-watch/styles_material.xml
new file mode 100644
index 0000000..cd59902
--- /dev/null
+++ b/core/res/res/values-bn-rBD-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"প্রার্থীরা"</font></string>
+</resources>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index 2d88a1d..c9e921a 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"সঠিক!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"আবার চেষ্টা করুন"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"আবার চেষ্টা করুন"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"সমস্ত বৈশিষ্ট্য এবং ডেটার জন্য আনলক করুন"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"মুখের সাহায্যে আনলক করার প্রচেষ্টা যতবার করা যায় তার সীমা পেরিয়ে গেছে"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"কোনো সিম কার্ড নেই"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"ট্যাবলেটের মধ্যে কোনো সিম কার্ড নেই৷"</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android আপগ্রেড করা হচ্ছে..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android চালু হচ্ছে…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"সঞ্চয়স্থান অপ্টিমাইজ করা হচ্ছে৷"</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android আপগ্রেড করা হচ্ছে"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android আপডেট সম্পন্ন করা হচ্ছে…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"আপগ্রেড সম্পন্ন না হওয়া পর্যন্ত কিছু অ্যাপ্লিকেশান সঠিকভাবে কাজ নাও করতে পারে"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> আপগ্রেড করা হচ্ছে…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g>টির মধ্যে <xliff:g id="NUMBER_0">%1$d</xliff:g>টি অ্যাপ্লিকেশান অপ্টিমাইজ করা হচ্ছে৷"</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> প্রস্তুত করা হচ্ছে৷"</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"অ্যাপ্লিকেশানগুলি শুরু করা হচ্ছে৷"</string>
@@ -1068,8 +1070,8 @@
<item quantity="one">খোলা ওয়াই-ফাই নেটওয়ার্কগুলি উপলব্ধ রয়েছে</item>
<item quantity="other">খোলা ওয়াই-ফাই নেটওয়ার্কগুলি উপলব্ধ রয়েছে</item>
</plurals>
- <string name="wifi_available_sign_in" msgid="9157196203958866662">"ওয়াই-ফাই নেটওয়ার্কে প্রবেশ করুন করুন"</string>
- <string name="network_available_sign_in" msgid="1848877297365446605">"নেটওয়ার্কে প্রবেশ করুন করুন"</string>
+ <string name="wifi_available_sign_in" msgid="9157196203958866662">"ওয়াই-ফাই নেটওয়ার্কে প্রবেশ করুন"</string>
+ <string name="network_available_sign_in" msgid="1848877297365446605">"নেটওয়ার্কে প্রবেশ করুন"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
<skip />
<string name="wifi_no_internet" msgid="8451173622563841546">"ওয়াই-ফাই -তে কোনো ইন্টারনেট অ্যাক্সেস নেই"</string>
@@ -1399,7 +1401,7 @@
<string name="kg_login_instructions" msgid="1100551261265506448">"আনলক করতে আপনার Google অ্যাকাউন্টের মাধ্যমে প্রবেশ করুন করুন৷"</string>
<string name="kg_login_username_hint" msgid="5718534272070920364">"ব্যবহারকারী নাম (ইমেল)"</string>
<string name="kg_login_password_hint" msgid="9057289103827298549">"পাসওয়ার্ড"</string>
- <string name="kg_login_submit_button" msgid="5355904582674054702">"প্রবেশ করুন করুন"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"প্রবেশ করুন"</string>
<string name="kg_login_invalid_input" msgid="5754664119319872197">"অবৈধ ব্যবহারকারী নাম অথবা পাসওয়ার্ড৷"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"আপনার ব্যবহারকারী নাম অথবা পাসওয়ার্ড ভুলে গেছেন?\n"<b>"google.com/accounts/recovery"</b>" এ যান৷"</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"অ্যাকাউন্ট পরীক্ষা করা হচ্ছে..."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"আনপিন করুন"</string>
<string name="app_info" msgid="6856026610594615344">"অ্যাপ্লিকেশানের তথ্য"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"ডিভাইস পুনরায় সেট করবেন?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"ডিভাইসটিকে পুনরায় সেট করতে আলতো চাপুন"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"ডেমো শুরু করা হচ্ছে…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"ডিভাইস পুনরায় সেট করা হচ্ছে…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"ডিভাইস পুনরায় সেট করবেন?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"আপনার করা যে কোনো পরিবর্তন মুছে যাবে এবং <xliff:g id="TIMEOUT">%1$s</xliff:g> সেকেন্ডের মধ্যে ডেমো আবার শুরু হবে…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"বাতিল করুন"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"এখনই পুনরায় সেট করুন"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"কোনো বিধিনিষেধ ছাড়াই এই ডিভাইসটিকে ব্যবহার করতে ফ্যাক্টরি রিসেট করুন"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"আরো জানতে স্পর্শ করুন৷"</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"অক্ষম করা <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"কনফারেন্স কল"</string>
</resources>
diff --git a/core/res/res/values-bs-rBA-watch/styles_material.xml b/core/res/res/values-bs-rBA-watch/styles_material.xml
new file mode 100644
index 0000000..88e5751
--- /dev/null
+++ b/core/res/res/values-bs-rBA-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"kandidati"</font></string>
+</resources>
diff --git a/core/res/res/values-bs-rBA/strings.xml b/core/res/res/values-bs-rBA/strings.xml
index 837e2bb..20104a6 100644
--- a/core/res/res/values-bs-rBA/strings.xml
+++ b/core/res/res/values-bs-rBA/strings.xml
@@ -681,6 +681,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Ispravno!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Pokušajte ponovo"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Pokušajte ponovo"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Otključajte uređaj za sve funkcije i podatke"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Premašen maksimalni broj pokušaja otključavanja licem"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Nema SIM kartice"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Nema SIM kartice u tabletu."</string>
@@ -1047,8 +1048,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Nadogradnja sistema Android u toku..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android se pokreće..."</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimiziranje pohrane."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android se nadograđuje"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Dovršava se ažuriranje Androida…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Neke aplikacije možda neće raditi ispravno dok traje nadogradnja"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"Aplikacija <xliff:g id="APPLICATION">%1$s</xliff:g> se nadograđuje…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optimiziranje aplikacije <xliff:g id="NUMBER_0">%1$d</xliff:g> od <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Priprema se <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Pokretanje aplikacija."</string>
@@ -1163,8 +1165,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB za MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Uspostavljena veza sa USB pohranom"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Dodirnite za više opcija."</string>
- <string name="adb_active_notification_title" msgid="6729044778949189918">"Uređaj za USB otklanjanje grešaka povezan"</string>
- <string name="adb_active_notification_message" msgid="4948470599328424059">"Dodirnite da onemogućite otklanjanje grešaka preko USB veze."</string>
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"Otklanjanje grešaka putem uređaja spojenog na USB je uspostavljeno"</string>
+ <string name="adb_active_notification_message" msgid="4948470599328424059">"Dodirnite da onemogućite otklanjanje grešaka putem uređaja spojenog na USB."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Prijem izvještaja o grešci..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Podijeliti izvještaj o grešci?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Dijeljenje izvještaja o grešci..."</string>
@@ -1689,7 +1691,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Otkači"</string>
<string name="app_info" msgid="6856026610594615344">"Informacije o aplikaciji"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Želite li vratiti na početne postavke?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Dodirnite da vratite uređaj na početne postavke"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Pokretanje demonstracije…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Vraćanje uređaja na početne postavke…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Želite li vratiti na početne postavke?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Nestat će sve izmjene, a demonstracija će početi ponovo za <xliff:g id="TIMEOUT">%1$s</xliff:g> sek…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Otkaži"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Vrati sada na početne postavke"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Vratite uređaj na fabričke postavke kako biste ga koristili bez ograničenja"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Dodirnite da saznate više."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Onemogućen <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Konferencijski poziv"</string>
</resources>
diff --git a/core/res/res/values-ca-watch/styles_material.xml b/core/res/res/values-ca-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-ca-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index e5a815d..eeed892 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correcte!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Torna-ho a provar"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Torna-ho a provar"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Desbl. per accedir a totes les funcions i dades"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"S\'ha superat el nombre màxim d\'intents de desbloqueig facial"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"No hi ha cap targeta SIM."</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"No hi ha cap targeta SIM a la tauleta."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android s\'està actualitzant..."</string>
<string name="android_start_title" msgid="8418054686415318207">"S\'està iniciant Android…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"S\'està optimitzant l\'emmagatzematge."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android s\'està actualitzant"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"S\'està acabant d\'actualitzar Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Pot ser que algunes aplicacions no funcionin correctament fins que no es completi l\'actualització"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"S\'està actualitzant <xliff:g id="APPLICATION">%1$s</xliff:g>…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"S\'està optimitzant l\'aplicació <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"S\'està preparant <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"S\'estan iniciant les aplicacions."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"No fixis"</string>
<string name="app_info" msgid="6856026610594615344">"Informació de l\'aplicació"</string>
<string name="negative_duration" msgid="5688706061127375131">"-<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Vols restablir el dispositiu?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Toca per restablir el dispositiu"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"S\'està iniciant la demostració…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"S\'està restablint el dispositiu…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Vols restablir el dispositiu?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Perdràs els canvis, i la demostració tornarà a començar d\'aquí a <xliff:g id="TIMEOUT">%1$s</xliff:g> segons…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Cancel·la"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Restableix ara"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Restableix les dades de fàbrica del dispositiu per utilitzar-lo sense restriccions"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Toca per obtenir més informació."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> s\'ha desactivat"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Conferència"</string>
</resources>
diff --git a/core/res/res/values-cs-watch/styles_material.xml b/core/res/res/values-cs-watch/styles_material.xml
new file mode 100644
index 0000000..5b604e8
--- /dev/null
+++ b/core/res/res/values-cs-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"kandidáti"</font></string>
+</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 4a142a2..df8bffd 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -684,6 +684,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Správně!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Zkusit znovu"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Zkusit znovu"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Funkce a data jsou k dispozici po odemčení"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Překročili jste maximální povolený počet pokusů o odemknutí obličejem."</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Není vložena SIM karta"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"V tabletu není SIM karta."</string>
@@ -1068,8 +1069,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android se upgraduje..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Spouštění systému Android…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Probíhá optimalizace úložiště."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android se upgraduje"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Dokončování aktualizace Androidu…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Před dokončením upgradu nemusí některé aplikace fungovat správně"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"Aplikace <xliff:g id="APPLICATION">%1$s</xliff:g> se upgraduje…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optimalizování aplikace <xliff:g id="NUMBER_0">%1$d</xliff:g> z <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Příprava aplikace <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Spouštění aplikací."</string>
@@ -1723,7 +1725,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Odepnout"</string>
<string name="app_info" msgid="6856026610594615344">"Informace o aplikaci"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Resetovat zařízení?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Zařízení resetujete klepnutím"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Spouštění ukázky…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Resetování zařízení…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Resetovat zařízení?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Ztratíte všechny provedené změny a ukázka se za <xliff:g id="TIMEOUT">%1$s</xliff:g> s spustí znovu…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Zrušit"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Resetovat"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Chcete-li toto zařízení používat bez omezení, obnovte jej do továrního nastavení"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Klepnutím zobrazíte další informace."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> – zakázáno"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Konferenční hovor"</string>
</resources>
diff --git a/core/res/res/values-da-watch/styles_material.xml b/core/res/res/values-da-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-da-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 762c173..731c847 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -245,7 +245,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktpersoner"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"have adgang til dine kontaktpersoner"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Placering"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"få adgang til enhedens placering"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"at få adgang til enhedens placering"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"have adgang til din kalender"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"Sms"</string>
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Rigtigt!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Prøv igen"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Prøv igen"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Lås op for at se alle funktioner og data"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Det maksimale antal forsøg på at bruge Ansigtslås er overskredet"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Intet SIM-kort"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Der er ikke noget SIM-kort i tabletcomputeren."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android opgraderes..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android starter..."</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Lageret optimeres."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android opgraderes"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Afslutter Android-opdateringen…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Nogle apps fungerer muligvis ikke korrekt, før opgraderingen er gennemført"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> opgraderer…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optimerer app <xliff:g id="NUMBER_0">%1$d</xliff:g> ud af <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Forbereder <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Åbner dine apps."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Frigør"</string>
<string name="app_info" msgid="6856026610594615344">"Oplysninger om appen"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Vil du nulstille enheden?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Tryk for at nulstille enheden"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Starter demoen…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Nulstiller enheden…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Vil du nulstille enheden?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Du mister alle ændringer, og demoen starter igen om <xliff:g id="TIMEOUT">%1$s</xliff:g> sekunder…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Annuller"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Nulstil nu"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Gendan fabriksdataene på enheden for at bruge den uden begrænsninger"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Tryk for at få flere oplysninger."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> – deaktiveret"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Telefonmøde"</string>
</resources>
diff --git a/core/res/res/values-de-watch/styles_material.xml b/core/res/res/values-de-watch/styles_material.xml
new file mode 100644
index 0000000..891a647
--- /dev/null
+++ b/core/res/res/values-de-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"Kandidaten"</font></string>
+</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index b163d35..c478607 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Korrekt!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Erneut versuchen"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Erneut versuchen"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Entsperren, um alle Funktionen und Daten zu nutzen"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Die maximal zulässige Anzahl an Face Unlock-Versuchen wurde überschritten."</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Keine SIM-Karte"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Keine SIM-Karte im Tablet"</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android wird aktualisiert..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android wird gestartet…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Speicher wird optimiert"</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android wird aktualisiert"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android-Update wird beendet…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Einige Apps funktionieren unter Umständen nicht richtig, bis das Upgrade abgeschlossen ist"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"Für <xliff:g id="APPLICATION">%1$s</xliff:g> wird gerade ein Upgrade ausgeführt…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> von <xliff:g id="NUMBER_1">%2$d</xliff:g> wird optimiert..."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> wird vorbereitet"</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Apps werden gestartet..."</string>
@@ -1213,7 +1215,7 @@
<string name="deny" msgid="2081879885755434506">"Ablehnen"</string>
<string name="permission_request_notification_title" msgid="6486759795926237907">"Berechtigung angefordert"</string>
<string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"Berechtigung angefordert\nfür Konto <xliff:g id="ACCOUNT">%s</xliff:g>"</string>
- <string name="forward_intent_to_owner" msgid="1207197447013960896">"Du verwendest diese App außerhalb deines Arbeitsprofils."</string>
+ <string name="forward_intent_to_owner" msgid="1207197447013960896">"Sie verwenden diese App außerhalb Ihres Arbeitsprofils"</string>
<string name="forward_intent_to_work" msgid="621480743856004612">"Du verwendest diese App in deinem Arbeitsprofil."</string>
<string name="input_method_binding_label" msgid="1283557179944992649">"Eingabemethode"</string>
<string name="sync_binding_label" msgid="3687969138375092423">"Synchronisieren"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Markierung entfernen"</string>
<string name="app_info" msgid="6856026610594615344">"App-Informationen"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Gerät zurücksetzen?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Zum Zurücksetzen des Geräts tippen"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Demo wird gestartet…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Gerät wird zurückgesetzt…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Gerät zurücksetzen?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Alle Änderungen gehen verloren und Demo wird in <xliff:g id="TIMEOUT">%1$s</xliff:g> Sekunden neu gestartet…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Abbrechen"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Jetzt zurücksetzen"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Gerät auf Werkseinstellungen zurücksetzen, um es ohne Einschränkungen zu nutzen"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Für weitere Informationen tippen."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> deaktiviert"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Telefonkonferenz"</string>
</resources>
diff --git a/core/res/res/values-el-watch/styles_material.xml b/core/res/res/values-el-watch/styles_material.xml
new file mode 100644
index 0000000..a02b85e
--- /dev/null
+++ b/core/res/res/values-el-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"υποψήφιοι"</font></string>
+</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 100ee81..3c6e832 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Σωστό!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Προσπαθήστε ξανά"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Προσπαθήστε ξανά"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Ξεκλείδωμα για όλες τις λειτουργίες και δεδομένα"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Έγινε υπέρβαση του μέγιστου αριθμού προσπαθειών Face Unlock"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Δεν υπάρχει κάρτα SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Δεν υπάρχει κάρτα SIM στο tablet."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Το Android αναβαθμίζεται..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Εκκίνηση Android…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Βελτιστοποίηση αποθηκευτικού χώρου."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Το Android αναβαθμίζεται"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Ολοκλήρωση ενημέρωσης Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Ορισμένες εφαρμογές ενδέχεται να μην λειτουργούν σωστά μέχρι την ολοκλήρωση της αναβάθμισης"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"Η εφαρμογή <xliff:g id="APPLICATION">%1$s</xliff:g> αναβαθμίζεται…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Βελτιστοποίηση της εφαρμογής <xliff:g id="NUMBER_0">%1$d</xliff:g> από <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Προετοιμασία <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Έναρξη εφαρμογών."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Ξεκαρφίτσωμα"</string>
<string name="app_info" msgid="6856026610594615344">"Πληροφορίες εφαρμογής"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Να γίνει επαναφορά της συσκευής;"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Πατήστε για επαναφορά της συσκευής"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Έναρξη επίδειξης…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Επαναφορά συσκευής…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Να γίνει επαναφορά της συσκευής;"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Τυχόν αλλαγές που πραγματοποιήσατε θα χαθούν και η επίδειξη θα ξεκινήσει ξανά σε <xliff:g id="TIMEOUT">%1$s</xliff:g> δευτερόλεπτα…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Ακύρωση"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Επαναφορά τώρα"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Επαναφέρετε τις εργοστασιακές ρυθμίσεις για να χρησιμοποιήσετε αυτήν τη συσκευή χωρίς περιορισμούς"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Αγγίξτε για να μάθετε περισσότερα."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Απενεργοποιημένο <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Κλήση συνδιάσκεψης"</string>
</resources>
diff --git a/core/res/res/values-en-rAU-watch/styles_material.xml b/core/res/res/values-en-rAU-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-en-rAU-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 0522882..12b7aa5 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correct!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Try again"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Try again"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Unlock for all features and data"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maximum Face Unlock attempts exceeded"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"No SIM card"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"No SIM card in tablet."</string>
@@ -974,7 +975,7 @@
<string name="whichEditApplicationLabel" msgid="7183524181625290300">"Edit"</string>
<string name="whichSendApplication" msgid="6902512414057341668">"Share with"</string>
<string name="whichSendApplicationNamed" msgid="2799370240005424391">"Share with %1$s"</string>
- <string name="whichSendApplicationLabel" msgid="4579076294675975354">"Shared"</string>
+ <string name="whichSendApplicationLabel" msgid="4579076294675975354">"Share"</string>
<string name="whichSendToApplication" msgid="8272422260066642057">"Send using"</string>
<string name="whichSendToApplicationNamed" msgid="7768387871529295325">"Send using %1$s"</string>
<string name="whichSendToApplicationLabel" msgid="8878962419005813500">"Send"</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android is upgrading…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android is starting…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimising storage."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android is upgrading"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Finishing Android update…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Some apps may not work properly until the upgrade finishes"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> is upgrading…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optimising app <xliff:g id="NUMBER_0">%1$d</xliff:g> of <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Preparing <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Starting apps."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Unpin"</string>
<string name="app_info" msgid="6856026610594615344">"App info"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Reset device?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Tap to reset device"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Starting demo…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Resetting device…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Reset device?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"You\'ll lose any changes and the demo will start again in <xliff:g id="TIMEOUT">%1$s</xliff:g> seconds…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Cancel"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Reset now"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Factory reset to use this device without restrictions"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Touch to find out more."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Disabled <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Conference Call"</string>
</resources>
diff --git a/core/res/res/values-en-rGB-watch/styles_material.xml b/core/res/res/values-en-rGB-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-en-rGB-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 0522882..12b7aa5 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correct!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Try again"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Try again"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Unlock for all features and data"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maximum Face Unlock attempts exceeded"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"No SIM card"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"No SIM card in tablet."</string>
@@ -974,7 +975,7 @@
<string name="whichEditApplicationLabel" msgid="7183524181625290300">"Edit"</string>
<string name="whichSendApplication" msgid="6902512414057341668">"Share with"</string>
<string name="whichSendApplicationNamed" msgid="2799370240005424391">"Share with %1$s"</string>
- <string name="whichSendApplicationLabel" msgid="4579076294675975354">"Shared"</string>
+ <string name="whichSendApplicationLabel" msgid="4579076294675975354">"Share"</string>
<string name="whichSendToApplication" msgid="8272422260066642057">"Send using"</string>
<string name="whichSendToApplicationNamed" msgid="7768387871529295325">"Send using %1$s"</string>
<string name="whichSendToApplicationLabel" msgid="8878962419005813500">"Send"</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android is upgrading…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android is starting…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimising storage."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android is upgrading"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Finishing Android update…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Some apps may not work properly until the upgrade finishes"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> is upgrading…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optimising app <xliff:g id="NUMBER_0">%1$d</xliff:g> of <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Preparing <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Starting apps."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Unpin"</string>
<string name="app_info" msgid="6856026610594615344">"App info"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Reset device?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Tap to reset device"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Starting demo…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Resetting device…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Reset device?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"You\'ll lose any changes and the demo will start again in <xliff:g id="TIMEOUT">%1$s</xliff:g> seconds…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Cancel"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Reset now"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Factory reset to use this device without restrictions"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Touch to find out more."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Disabled <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Conference Call"</string>
</resources>
diff --git a/core/res/res/values-en-rIN-watch/styles_material.xml b/core/res/res/values-en-rIN-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-en-rIN-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 0522882..12b7aa5 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correct!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Try again"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Try again"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Unlock for all features and data"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maximum Face Unlock attempts exceeded"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"No SIM card"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"No SIM card in tablet."</string>
@@ -974,7 +975,7 @@
<string name="whichEditApplicationLabel" msgid="7183524181625290300">"Edit"</string>
<string name="whichSendApplication" msgid="6902512414057341668">"Share with"</string>
<string name="whichSendApplicationNamed" msgid="2799370240005424391">"Share with %1$s"</string>
- <string name="whichSendApplicationLabel" msgid="4579076294675975354">"Shared"</string>
+ <string name="whichSendApplicationLabel" msgid="4579076294675975354">"Share"</string>
<string name="whichSendToApplication" msgid="8272422260066642057">"Send using"</string>
<string name="whichSendToApplicationNamed" msgid="7768387871529295325">"Send using %1$s"</string>
<string name="whichSendToApplicationLabel" msgid="8878962419005813500">"Send"</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android is upgrading…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android is starting…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimising storage."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android is upgrading"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Finishing Android update…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Some apps may not work properly until the upgrade finishes"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> is upgrading…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optimising app <xliff:g id="NUMBER_0">%1$d</xliff:g> of <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Preparing <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Starting apps."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Unpin"</string>
<string name="app_info" msgid="6856026610594615344">"App info"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Reset device?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Tap to reset device"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Starting demo…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Resetting device…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Reset device?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"You\'ll lose any changes and the demo will start again in <xliff:g id="TIMEOUT">%1$s</xliff:g> seconds…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Cancel"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Reset now"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Factory reset to use this device without restrictions"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Touch to find out more."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Disabled <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Conference Call"</string>
</resources>
diff --git a/core/res/res/values-es-rUS-watch/styles_material.xml b/core/res/res/values-es-rUS-watch/styles_material.xml
new file mode 100644
index 0000000..898d2fd
--- /dev/null
+++ b/core/res/res/values-es-rUS-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidatos"</font></string>
+</resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 0786b1a..136902b 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correcto"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Vuelve a intentarlo."</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Volver a intentarlo"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Desbloquea para acceder a funciones y datos"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Se superó el máximo de intentos permitido para el desbloqueo facial del dispositivo."</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Sin tarjeta SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"No hay tarjeta SIM en el tablet."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android se está actualizando..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Iniciando Android…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimizando almacenamiento"</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android se está actualizando"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Finalizando actualización de Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Es posible que algunas apps no funcionen correctamente hasta que termine la actualización"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"Se está actualizando <xliff:g id="APPLICATION">%1$s</xliff:g>…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optimizando la aplicación <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>"</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Preparando <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Iniciando aplicaciones"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"No fijar"</string>
<string name="app_info" msgid="6856026610594615344">"Información de la app"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"¿Deseas restablecer el dispositivo?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Presiona para restablecer el dispositivo"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Iniciando demostración…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Restableciendo dispositivo…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"¿Deseas restablecer el dispositivo?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Se perderán los cambios y la demostración volverá a iniciarse en <xliff:g id="TIMEOUT">%1$s</xliff:g> segundos…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Cancelar"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Restablecer ahora"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Restablece la configuración de fábrica para usar este dispositivo sin restricciones"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Toca para obtener más información."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Se inhabilitó <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Conferencia"</string>
</resources>
diff --git a/core/res/res/values-es-watch/styles_material.xml b/core/res/res/values-es-watch/styles_material.xml
new file mode 100644
index 0000000..898d2fd
--- /dev/null
+++ b/core/res/res/values-es-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidatos"</font></string>
+</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 1909e54..c4ebb02 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correcto"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Vuelve a intentarlo"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Vuelve a intentarlo"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Desbloquear para todos los datos y funciones"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Se ha superado el número máximo de intentos de desbloqueo facial."</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Falta la tarjeta SIM."</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"No se ha insertado ninguna tarjeta SIM en el tablet."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Actualizando Android"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android se está iniciando…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimizando almacenamiento."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Actualizando Android"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Terminando de actualizar Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Es posible que algunas aplicaciones no funcionen correctamente hasta que finalice la actualización"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> se está actualizando…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optimizando aplicación <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>..."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Preparando <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Iniciando aplicaciones"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"No fijar"</string>
<string name="app_info" msgid="6856026610594615344">"Información de la aplicación"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"¿Restablecer el dispositivo?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Toca para restablecer el dispositivo"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Iniciando demostración…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Restableciendo dispositivo…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"¿Restablecer el dispositivo?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Se perderán todos los cambios y la demostración volverá a empezar en <xliff:g id="TIMEOUT">%1$s</xliff:g> segundos…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Cancelar"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Restablecer ahora"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Restablece los datos de fábrica para usar este dispositivo sin restricciones"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Toca para obtener más información."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> inhabilitado"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Conferencia"</string>
</resources>
diff --git a/core/res/res/values-et-rEE-watch/styles_material.xml b/core/res/res/values-et-rEE-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-et-rEE-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 14695e3..8922379 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Õige."</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Proovige uuesti"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Proovige uuesti"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Ava kõigi funktsioonide ja andmete nägemiseks"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maksimaalne teenusega Face Unlock avamise katsete arv on ületatud"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"SIM-kaarti pole"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Tahvelarvutis pole SIM-kaarti."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android viiakse üle uuemale versioonile ..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android käivitub ..."</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Salvestusruumi optimeerimine."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android viiakse üle uuemale versioonile"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Androidi värskenduse lõpetamine …"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Mõned rakendused ei pruugi enne uuemale versioonile ülemineku lõpetamist korralikult töötada"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"Rakenduse <xliff:g id="APPLICATION">%1$s</xliff:g> versiooni uuendatakse …"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_0">%1$d</xliff:g>. rakenduse <xliff:g id="NUMBER_1">%2$d</xliff:g>-st optimeerimine."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Rakenduse <xliff:g id="APPNAME">%1$s</xliff:g> ettevalmistamine."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Rakenduste käivitamine."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Vabasta"</string>
<string name="app_info" msgid="6856026610594615344">"Rakenduse teave"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Kas soovite seadme lähtestada?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Puudutage seadme lähtestamiseks"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Demo käivitamine …"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Seadme lähtestamine …"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Kas soovite seadme lähtestada?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Kõik muudatused lähevad kaotsi ja demo käivitub uuesti <xliff:g id="TIMEOUT">%1$s</xliff:g> sekundi möödudes …"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Tühista"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Lähtesta kohe"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Seadme piiranguteta kasutamiseks lähtestage see tehaseandmetele"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Lisateabe saamiseks puudutage."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Keelatud <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Konverentskõne"</string>
</resources>
diff --git a/core/res/res/values-eu-rES-watch/styles_material.xml b/core/res/res/values-eu-rES-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-eu-rES-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index 98adc86..7dc0877c 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -439,7 +439,7 @@
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Hatz-marka digitala ez da osorik hauteman. Saiatu berriro."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Ezin izan da hatza-marka prozesatu. Saiatu berriro."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Hatz-marka digitalen sentsorea zikina dago. Garbi ezazu, eta saiatu berriro."</string>
- <string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Hatza bizkorregi mugitu duzu. Saiatu berriro."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Hatza azkarregi mugitu duzu. Saiatu berriro."</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Mantsoegi mugitu duzu hatza. Saiatu berriro."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Eredua zuzena da!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Saiatu berriro"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Saiatu berriro"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Desblokeatu eginbide eta datu guztiak erabiltzeko"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Aurpegiaren bidez desblokeatzeko saiakera muga gainditu da"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Ez dago SIM txartelik"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Ez dago SIM txartelik tabletan."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android bertsio-berritzen ari da…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android abiarazten ari da…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Memoria optimizatzen."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android bertsioa berritzen ari gara"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android eguneratzen amaitzen…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Aplikazio batzuek agian ez dute behar bezala funtzionatuko bertsioa berritzen amaitu arte"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> bertsio-berritzen ari da…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g> aplikazio optimizatzen."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> prestatzen."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Aplikazioak abiarazten."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Kendu aingura"</string>
<string name="app_info" msgid="6856026610594615344">"Aplikazioari buruzko informazioa"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Gailua berrezarri nahi duzu?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Gailua berrezartzeko, sakatu hau"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Demoa abiarazten…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Gailua berrezartzen…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Gailua berrezarri nahi duzu?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Aldaketak galduko dituzu eta <xliff:g id="TIMEOUT">%1$s</xliff:g> segundo barru hasiko da berriro demoa…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Utzi"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Berrezarri"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Berrezarri jatorrizko ezarpenak gailua murriztapenik gabe erabili ahal izateko"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Sakatu informazio gehiago lortzeko."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> desgaituta dago"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Konferentzia-deia"</string>
</resources>
diff --git a/core/res/res/values-fa-watch/styles_material.xml b/core/res/res/values-fa-watch/styles_material.xml
new file mode 100644
index 0000000..23b9a7e
--- /dev/null
+++ b/core/res/res/values-fa-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"کاندیدها"</font></string>
+</resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 36ba8e7..5976ad1 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -127,7 +127,7 @@
<item msgid="2254967670088539682">"برای برقراری تماس و ارسال پیام از طریق Wi-Fi، ابتدا از شرکت مخابراتیتان درخواست کنید این سرویس را راهاندازی کند. سپس دوباره از تنظیمات، تماس Wi-Fi را روشن کنید."</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
- <item msgid="6177300162212449033">"ثبت نام با شرکت مخابراتی شما"</item>
+ <item msgid="6177300162212449033">"ثبتنام با شرکت مخابراتی شما"</item>
</string-array>
<string-array name="wfcSpnFormats">
<item msgid="6830082633573257149">"%s"</item>
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"صحیح است!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"دوباره امتحان کنید"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"دوباره امتحان کنید"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"باز کردن قفل تمام قابلیتها و دادهها"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"دفعات تلاش برای Face Unlock از حداکثر مجاز بیشتر شد"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"سیم کارت موجود نیست"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"سیم کارت درون رایانهٔ لوحی نیست."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android در حال ارتقا است..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android در حال راهاندازی است..."</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"بهینهسازی فضای ذخیرهسازی."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android درحال ارتقا است"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"درحال پایان بهروزرسانی Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"تا پایان ارتقا، ممکن است برخی از برنامهها بهدرستی کار نکنند."</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> درحال ارتقا است...."</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"در حال بهینهسازی برنامهٔ <xliff:g id="NUMBER_0">%1$d</xliff:g> از <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"آمادهسازی <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"در حال آغاز برنامهها."</string>
@@ -1510,8 +1512,8 @@
<string name="mediasize_japanese_kahu" msgid="6872696027560065173">"Kahu"</string>
<string name="mediasize_japanese_kaku2" msgid="2359077233775455405">"Kaku2"</string>
<string name="mediasize_japanese_you4" msgid="2091777168747058008">"You4"</string>
- <string name="mediasize_unknown_portrait" msgid="3088043641616409762">"عمودی ناشناخته"</string>
- <string name="mediasize_unknown_landscape" msgid="4876995327029361552">"افقی ناشناخته"</string>
+ <string name="mediasize_unknown_portrait" msgid="3088043641616409762">"عمودی ناشناس"</string>
+ <string name="mediasize_unknown_landscape" msgid="4876995327029361552">"افقی ناشناس"</string>
<string name="write_fail_reason_cancelled" msgid="7091258378121627624">"لغو شد"</string>
<string name="write_fail_reason_cannot_write" msgid="8132505417935337724">"خطا هنگام نوشتن محتوا"</string>
<string name="reason_unknown" msgid="6048913880184628119">"نامعلوم"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"برداشتن پین"</string>
<string name="app_info" msgid="6856026610594615344">"اطلاعات برنامه"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"دستگاه بازنشانی شود؟"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"برای بازنشانی دستگاه، ضربه بزنید"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"در حال شروع نسخه نمایشی…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"در حال بازنشانی دستگاه…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"دستگاه بازنشانی شود؟"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"همه تغییرات را از دست خواهید داد و نسخه نمایشی دوباره تا <xliff:g id="TIMEOUT">%1$s</xliff:g> ثانیه دیگر شروع میشود…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"لغو"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"بازنشانی در این لحظه"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"برای استفاده بدون محدودیت از این دستگاه، بازنشانی کارخانهای انجام دهید"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"برای یادگیری بیشتر لمس کنید."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> غیرفعال شد"</string>
+ <string name="conference_call" msgid="3751093130790472426">"تماس کنفرانسی"</string>
</resources>
diff --git a/core/res/res/values-fi-watch/styles_material.xml b/core/res/res/values-fi-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-fi-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index ac827a1..b221750 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Oikein!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Yritä uudelleen"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Yritä uudelleen"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Käytä kaikkia ominaisuuksia avaamalla lukitus."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Face Unlock -yrityksiä tehty suurin sallittu määrä."</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Ei SIM-korttia"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Tablet-laitteessa ei ole SIM-korttia."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Androidia päivitetään…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android käynnistyy…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimoidaan tallennustilaa."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Androidia päivitetään"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Viimeistellään Android-päivitystä…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Kaikki sovellukset eivät ehkä toimi oikein, ennen kuin päivitys on valmis."</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> päivittyy…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optimoidaan sovellusta <xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Valmistellaan: <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Käynnistetään sovelluksia."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Irrota"</string>
<string name="app_info" msgid="6856026610594615344">"Sovelluksen tiedot"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Palautetaanko laitteen tehdasasetukset?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Palauta laite napauttamalla"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Aloitetaan esittelyä…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Palautetaan asetuksia…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Palautetaanko laitteen tehdasasetukset?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Muutokset poistetaan ja esittely aloitetaan uudelleen <xliff:g id="TIMEOUT">%1$s</xliff:g> sekunnin kuluttua…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Peruuta"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Palauta nyt"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Palauta tehdasasetukset, jotta voit käyttää tätä laitetta rajoituksitta"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Lue lisätietoja koskettamalla."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> ei ole käytössä."</string>
+ <string name="conference_call" msgid="3751093130790472426">"Puhelinneuvottelu"</string>
</resources>
diff --git a/core/res/res/values-fr-rCA-watch/styles_material.xml b/core/res/res/values-fr-rCA-watch/styles_material.xml
new file mode 100644
index 0000000..f692d5c
--- /dev/null
+++ b/core/res/res/values-fr-rCA-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidats"</font></string>
+</resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 57119da..d9fbceb 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -21,7 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"B"</string>
- <string name="kilobyteShort" msgid="5973789783504771878">"Ko"</string>
+ <string name="kilobyteShort" msgid="5973789783504771878">"ko"</string>
<string name="megabyteShort" msgid="6355851576770428922">"Mo"</string>
<string name="gigabyteShort" msgid="3259882455212193214">"Go"</string>
<string name="terabyteShort" msgid="231613018159186962">"To"</string>
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"C\'est exact!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Réessayer"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Réessayer"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Déverr. pour acc. aux autres fonction. et données"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Nombre maximal autorisé de tentatives Face Unlock atteint."</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Aucune carte SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Aucune carte SIM n\'est insérée dans la tablette."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Mise à jour d\'Android…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android en cours de démarrage..."</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimisation du stockage."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Installation de la m. à niveau d\'Android"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Finalisation de la mise à jour d\'Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Il se peut que certaines applications ne fonctionnent pas correctement jusqu\'à ce que la mise à niveau soit terminée"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"Mise à niveau de <xliff:g id="APPLICATION">%1$s</xliff:g> en cours…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optimisation de l\'application <xliff:g id="NUMBER_0">%1$d</xliff:g> sur <xliff:g id="NUMBER_1">%2$d</xliff:g>…"</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Préparation de <xliff:g id="APPNAME">%1$s</xliff:g> en cours…"</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Lancement des applications…"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Annuler l\'épinglage"</string>
<string name="app_info" msgid="6856026610594615344">"Détails de l\'application"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Réinitialiser l\'appareil?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Touchez pour réinitialiser l\'appareil"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Démarrage de la démonstration en cours…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Réinitialisation de l\'appareil en cours…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Réinitialiser l\'appareil?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Vous perdrez vos modifications, et la démo recommencera dans <xliff:g id="TIMEOUT">%1$s</xliff:g> secondes…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Annuler"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Réinitialiser maintenant"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Rétablissez la configuration d\'usine de cet appareil pour l\'utiliser sans restrictions"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Touchez ici pour en savoir plus."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Désactivé : <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Conférence téléphonique"</string>
</resources>
diff --git a/core/res/res/values-fr-watch/styles_material.xml b/core/res/res/values-fr-watch/styles_material.xml
new file mode 100644
index 0000000..f692d5c
--- /dev/null
+++ b/core/res/res/values-fr-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidats"</font></string>
+</resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index b089991..6600f09 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Combinaison correcte !"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Veuillez réessayer."</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Veuillez réessayer."</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Déverr. pour autres fonctionnalités et données"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Nombre maximal autorisé de tentatives Face Unlock atteint."</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Pas de carte SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Aucune carte SIM n\'est insérée dans la tablette."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Mise à jour d\'Android…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Démarrage d\'Android en cours"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimisation du stockage en cours…"</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Mise à jour d\'Android…"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Finalisation de la mise à jour Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Certaines applications peuvent ne pas fonctionner correctement jusqu\'à ce que la mise à jour soit terminée."</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"Mise à jour de l\'application <xliff:g id="APPLICATION">%1$s</xliff:g>…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optimisation de l\'application <xliff:g id="NUMBER_0">%1$d</xliff:g> sur <xliff:g id="NUMBER_1">%2$d</xliff:g>…"</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Préparation de <xliff:g id="APPNAME">%1$s</xliff:g> en cours…"</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Lancement des applications…"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Retirer"</string>
<string name="app_info" msgid="6856026610594615344">"Infos sur l\'appli"</string>
<string name="negative_duration" msgid="5688706061127375131">"− <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Réinitialiser l\'appareil ?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Appuyer pour réinitialiser l\'appareil"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Lancement de la démo…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Réinitialisation…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Réinitialiser l\'appareil ?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Vous perdrez vos modifications, et la démo recommencera dans <xliff:g id="TIMEOUT">%1$s</xliff:g> secondes…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Annuler"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Réinitialiser maintenant"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Rétablir la configuration d\'usine pour utiliser cet appareil sans restrictions"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Appuyez ici pour en savoir plus."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Élément \"<xliff:g id="LABEL">%1$s</xliff:g>\" désactivé"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Conférence téléphonique"</string>
</resources>
diff --git a/core/res/res/values-gl-rES-watch/styles_material.xml b/core/res/res/values-gl-rES-watch/styles_material.xml
new file mode 100644
index 0000000..898d2fd
--- /dev/null
+++ b/core/res/res/values-gl-rES-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidatos"</font></string>
+</resources>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index 23669d4..8023edf 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -249,7 +249,7 @@
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendario"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"acceder ao teu calendario"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
- <string name="permgroupdesc_sms" msgid="4656988620100940350">"envíar e consultar mensaxes de SMS"</string>
+ <string name="permgroupdesc_sms" msgid="4656988620100940350">"enviar e consultar mensaxes de SMS"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Almacenamento"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"acceder a fotos, contido multimedia e ficheiros no teu dispositivo"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Micrófono"</string>
@@ -259,7 +259,7 @@
<string name="permgrouplab_phone" msgid="5229115638567440675">"Teléfono"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"facer e xestionar chamadas telefónicas"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Sensores corporais"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"accede aos datos do sensor sobre as túas constantes vitais"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acceder aos datos do sensor sobre as túas constantes vitais"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Recuperar contido da ventá"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Inspecciona o contido dunha ventá coa que estás interactuando."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Activar a exploración táctil"</string>
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correcto!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Téntao de novo"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Téntao de novo"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Desbloquea para gozar todas as funcións e datos"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Superouse o número máximo de intentos de desbloqueo facial"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Non hai ningunha tarxeta SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Non hai ningunha tarxeta SIM na tableta."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Estase actualizando Android…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Estase iniciando Android…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimizando almacenamento."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Estase actualizando Android"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Finalizando a actualización de Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"É posible que algunhas aplicacións non funcionen correctamente ata que finalice o proceso de actualización"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"Actualizando <xliff:g id="APPLICATION">%1$s</xliff:g>…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optimizando aplicación <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Preparando <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Iniciando aplicacións."</string>
@@ -1536,8 +1538,8 @@
<string name="immersive_cling_description" msgid="3482371193207536040">"Para saír, pasa o dedo cara abaixo desde arriba."</string>
<string name="immersive_cling_positive" msgid="5016839404568297683">"De acordo"</string>
<string name="done_label" msgid="2093726099505892398">"Feito"</string>
- <string name="hour_picker_description" msgid="6698199186859736512">"Control de desprazamento circular das horas"</string>
- <string name="minute_picker_description" msgid="8606010966873791190">"Control de desprazamento circular dos minutos"</string>
+ <string name="hour_picker_description" msgid="6698199186859736512">"Control desprazable circular das horas"</string>
+ <string name="minute_picker_description" msgid="8606010966873791190">"Control desprazable circular dos minutos"</string>
<string name="select_hours" msgid="6043079511766008245">"Seleccionar horas"</string>
<string name="select_minutes" msgid="3974345615920336087">"Seleccionar minutos"</string>
<string name="select_day" msgid="7774759604701773332">"Seleccionar mes e día"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Soltar"</string>
<string name="app_info" msgid="6856026610594615344">"Información da aplicación"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Queres restablecer o dispositivo?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Toca aquí para restablecer o dispositivo"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Iniciando demostración…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Restablecendo dispositivo…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Queres restablecer o dispositivo?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Perderás os cambios que fixeses e a demostración volverá comezar en <xliff:g id="TIMEOUT">%1$s</xliff:g> segundos…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Cancelar"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Restablecer agora"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Restablecemento dos valores de fábrica para usar este dispositivo sen restricións"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Toca para acceder a máis información"</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Desactivouse <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Conferencia telefónica"</string>
</resources>
diff --git a/core/res/res/values-gu-rIN-watch/styles_material.xml b/core/res/res/values-gu-rIN-watch/styles_material.xml
new file mode 100644
index 0000000..21c6871
--- /dev/null
+++ b/core/res/res/values-gu-rIN-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"ઉમેદવારો"</font></string>
+</resources>
diff --git a/core/res/res/values-gu-rIN/strings.xml b/core/res/res/values-gu-rIN/strings.xml
index 656061d..5c3bf93 100644
--- a/core/res/res/values-gu-rIN/strings.xml
+++ b/core/res/res/values-gu-rIN/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"સાચું!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"ફરી પ્રયાસ કરો"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"ફરી પ્રયાસ કરો"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"તમામ સુવિધાઓ અને ડેટા માટે અનલૉક કરો"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"મહત્તમ ફેસ અનલૉક પ્રયાસો ઓળંગાયા"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"કોઈ SIM કાર્ડ નથી"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"ટેબ્લેટમાં SIM કાર્ડ નથી."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android અપગ્રેડ થઈ રહ્યું છે..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android પ્રારંભ થઈ રહ્યું છે…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"સંગ્રહ ઓપ્ટિમાઇઝ કરી રહ્યું છે."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android અપગ્રેડ થઈ રહ્યું છે"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android અપડેટ સમાપ્ત કરી રહ્યાં છે…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"અપગ્રેડ સમાપ્ત ન થાય ત્યાં સુધી કેટલીક ઍપ્લિકેશનો કદાચ યોગ્ય રીતે કામ ન કરે"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> અપગ્રેડ થઈ રહી છે…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> માંથી <xliff:g id="NUMBER_0">%1$d</xliff:g> ઍપ્લિકેશન ઓપ્ટિમાઇઝ કરી રહ્યું છે."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> તૈયાર કરી રહ્યું છે."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"ઍપ્લિકેશનો શરૂ કરી રહ્યાં છે."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"અનપિન કરો"</string>
<string name="app_info" msgid="6856026610594615344">"ઍપ્લિકેશન માહિતી"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"ઉપકરણ ફરીથી સેટ કરીએ?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"ઉપકરણને ફરીથી સેટ કરવા માટે ટૅપ કરો"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"ડેમો પ્રારંભ કરી રહ્યાં છે…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"ઉપકરણ ફરીથી સેટ કરી રહ્યાં છે…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"ઉપકરણ ફરીથી સેટ કરીએ?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"તમે કોઈપણ ફેરફારો ગુમાવશો અને ડેમો <xliff:g id="TIMEOUT">%1$s</xliff:g> સેકન્ડમાં ફરી શરૂ થશે…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"રદ કરો"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"હમણાં ફરીથી સેટ કરો"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"આ ઉપકરણનો પ્રતિબંધો વિના ઉપયોગ કરવા માટે ફેક્ટરી રીસેટ કરો"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"વધુ જાણવા માટે ટચ કરો."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> અક્ષમ કર્યું"</string>
+ <string name="conference_call" msgid="3751093130790472426">"કોન્ફરન્સ કૉલ"</string>
</resources>
diff --git a/core/res/res/values-hi-watch/styles_material.xml b/core/res/res/values-hi-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-hi-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index b52ff25..bea243b 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"सही!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"फिर से प्रयास करें"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"फिर से प्रयास करें"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"सभी सुविधाओं और डेटा के लिए अनलॉक करें"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"फेस अनलॉक के अधिकतम प्रयासों की सीमा पार हो गई"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"कोई सिम कार्ड नहीं है"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"टेबलेट में कोई सिम कार्ड नहीं है."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android अपग्रेड हो रहा है..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android प्रारंभ हो रहा है…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"मेमोरी ऑप्टिमाइज़ हो रही है."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android अपग्रेड हो रहा है"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android अपडेट समाप्त हो रहा है…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"जब तक अपग्रेड पूरा नहीं हो जाता, तब तक संभव है कि कुछ ऐप्लिकेशन ठीक से कार्य ना करें"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> अपग्रेड हो रहा है…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> में से <xliff:g id="NUMBER_0">%1$d</xliff:g> ऐप्स अनुकूलित हो रहा है."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> तैयार हो रहा है."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"ऐप्स प्रारंभ होने वाले हैं"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"अनपिन करें"</string>
<string name="app_info" msgid="6856026610594615344">"ऐप की जानकारी"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"डिवाइस रीसेट करें?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"डिवाइस को रीसेट करने के लिए टैप करें"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"डेमो प्रारंभ हो रहा है…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"डिवाइस पुन: रीसेट कर रहा है…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"डिवाइस रीसेट करें?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"आपके सभी बदलाव खो जाएंगे और डेमो <xliff:g id="TIMEOUT">%1$s</xliff:g> सेकंड में फिर से शुरू हो जाएगा…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"अभी नहीं"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"अभी रीसेट करें"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"इस डिवाइस को प्रतिबंधों के बिना उपयोग करने के लिए फ़ैक्टरी रीसेट करें"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"अधिक जानने के लिए स्पर्श करें."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"अक्षम <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"कॉन्फ़्रेंस कॉल"</string>
</resources>
diff --git a/core/res/res/values-hr-watch/styles_material.xml b/core/res/res/values-hr-watch/styles_material.xml
new file mode 100644
index 0000000..88e5751
--- /dev/null
+++ b/core/res/res/values-hr-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"kandidati"</font></string>
+</resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index b90f9dd..b181f6d 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -681,6 +681,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Ispravno!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Pokušajte ponovo"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Pokušajte ponovo"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Otključajte za sve značajke i podatke"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Premašen je maksimalni broj Otključavanja licem"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Nema SIM kartice"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"U tabletnom uređaju nema SIM kartice."</string>
@@ -1045,8 +1046,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android se nadograđuje…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Pokretanje Androida..."</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimiziranje pohrane."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android se nadograđuje"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Dovršavanje ažuriranja Androida…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Neke aplikacije možda neće funkcionirati pravilno dok nadogradnja ne završi"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"Nadogradnja aplikacije <xliff:g id="APPLICATION">%1$s</xliff:g>…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optimiziranje aplikacije <xliff:g id="NUMBER_0">%1$d</xliff:g> od <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Pripremanje aplikacije <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Pokretanje aplikacija."</string>
@@ -1687,7 +1689,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Otkvači"</string>
<string name="app_info" msgid="6856026610594615344">"Informacije o aplikaciji"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Želite li vratiti uređaj na zadano?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Dodirnite za vraćanje uređaja na zadano"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Pokretanje demo-načina..."</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Vraćanje uređaja na zadano…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Želite li vratiti uređaj na zadano?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Sve će se promjene izbrisati, a demonstracija će se ponovo pokrenuti za <xliff:g id="TIMEOUT">%1$s</xliff:g> s…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Odustani"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Vrati na zadano sada"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Uređaj je vraćen na tvorničke postavke da biste ga mogli upotrebljavati bez ograničenja"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Dodirnite da biste saznali više."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> – onemogućeno"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Konferencijski poziv"</string>
</resources>
diff --git a/core/res/res/values-hu-watch/styles_material.xml b/core/res/res/values-hu-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-hu-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 8187e43..aea12a6 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Helyes!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Próbálja újra"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Újra"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Oldja fel a funkciók és adatok eléréséhez"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Elérte az arcalapú feloldási kísérletek maximális számát"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Nincs SIM-kártya."</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Nincs SIM-kártya a táblagépben."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android frissítése folyamatban..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Az Android indítása…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Tárhely-optimalizálás."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android frissítése folyamatban"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Az Android frissítésének befejezése…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"A frissítés befejezéséig előfordulhat, hogy egyes alkalmazások nem megfelelően működnek."</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"A(z) <xliff:g id="APPLICATION">%1$s</xliff:g> frissítése folyamatban van"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Alkalmazás optimalizálása: <xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"A(z) <xliff:g id="APPNAME">%1$s</xliff:g> előkészítése."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Kezdő alkalmazások."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Feloldás"</string>
<string name="app_info" msgid="6856026610594615344">"Alkalmazásinformáció"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Visszaállítja eszközét?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Koppintson az eszköz visszaállítása érdekében"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Bemutató indítása…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Eszköz visszaállítása…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Visszaállítja eszközét?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"A módosítások elvesznek, és a bemutató újra elindul <xliff:g id="TIMEOUT">%1$s</xliff:g> másodperc múlva…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Mégse"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Visszaállítás most"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Állítsa vissza a gyári beállításokat az eszköz korlátozások nélküli használata érdekében"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Érintse meg a további információkért."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"A(z) <xliff:g id="LABEL">%1$s</xliff:g> letiltva"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Konferenciahívás"</string>
</resources>
diff --git a/core/res/res/values-hy-rAM-watch/styles_material.xml b/core/res/res/values-hy-rAM-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-hy-rAM-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 2e557d1..ba97468 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -670,7 +670,7 @@
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Արտակարգ իրավիճակների հեռախոսահամար"</string>
<string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ծառայություն չկա"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Էկրանը կողպված է:"</string>
- <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Սեղմեք Ցանկ` ապակողպելու համար, կամ կատարեք արտակարգ իրավիճակների զանգ:"</string>
+ <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Ապակողպելու կամ շտապ կանչ անելու համար սեղմեք «Ընտրացանկ»"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Ապակողպելու համար սեղմեք Ցանկը:"</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Հավաքեք սխեման` ապակողպելու համար"</string>
<string name="lockscreen_emergency_call" msgid="5298642613417801888">"Արտակարգ իրավիճակ"</string>
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Ճիշտ է:"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Կրկին փորձեք"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Կրկին փորձեք"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Ապակողպեք՝ բոլոր գործառույթներն ու տվյալներն օգտագործելու համար"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Առավելագույն Դեմքով ապակողպման փորձերը գերազանցված են"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"SIM քարտ չկա"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Գրասալիկում SIM քարտ չկա:"</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android-ը նորացվում է..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android-ը մեկնարկում է…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Պահեստի օպտիմալացում:"</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android-ը նորացվում է"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android-ի թարմացումն ավարտվում է…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Հնարավոր է՝ որոշ հավելվածներ մինչև նորացման ավարտը ճիշտ չաշխատեն"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> հավելվածը նորացվում է…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Օպտիմալացվում է հավելված <xliff:g id="NUMBER_0">%1$d</xliff:g>-ը <xliff:g id="NUMBER_1">%2$d</xliff:g>-ից:"</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> հավելվածը պատրաստվում է:"</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Հավելվածները մեկնարկում են:"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Ապամրացնել"</string>
<string name="app_info" msgid="6856026610594615344">"Հավելվածի տվյալներ"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Վերակայե՞լ սարքը:"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Հպեք՝ սարքը վերակայելու համար"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Ցուցադրական օգտվողը գործարկվում է…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Սարաքը վերակայվում է…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Վերակայե՞լ սարքը:"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Կատարված փոփոխությունները չեն պահվի, իսկ ցուցադրական նյութը կրկին կգործարկվի <xliff:g id="TIMEOUT">%1$s</xliff:g> վայրկյանից…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Չեղարկել"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Վերակայել հիմա"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Սարքն առանց սահմանափակումների օգտագործելու համար կատարեք գործարանային վերակայում"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Հպեք՝ ավելին իմանալու համար:"</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Անջատած <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Կոնֆերանս զանգ"</string>
</resources>
diff --git a/core/res/res/values-in-watch/styles_material.xml b/core/res/res/values-in-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-in-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index bd17260..132789b 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Perbaiki!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Coba lagi"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Coba lagi"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Membuka kunci untuk semua fitur dan data"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Percobaan Face Unlock melebihi batas maksimum"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Tidak ada kartu SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Tidak ada kartu SIM dalam tablet."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android sedang meningkatkan versi..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Memulai Android…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Mengoptimalkan penyimpanan."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android sedang meningkatkan versi"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Menyelesaikan pembaruan Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Beberapa aplikasi mungkin tidak berfungsi dengan baik jika peningkatan versi belum selesai"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> sedang ditingkatkan versinya…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Mengoptimalkan aplikasi <xliff:g id="NUMBER_0">%1$d</xliff:g> dari <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Menyiapkan <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Memulai aplikasi."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Lepas pin"</string>
<string name="app_info" msgid="6856026610594615344">"Info aplikasi"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Setel ulang perangkat?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Ketuk untuk menyetel ulang perangkat"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Memulai demo..."</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Menyetel ulang perangkat..."</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Setel ulang perangkat?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Perubahan yang dibuat akan hilang dan demo akan dimulai lagi dalam <xliff:g id="TIMEOUT">%1$s</xliff:g> detik…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Batal"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Setel ulang sekarang"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Dikembalikan ke setelan pabrik agar perangkat ini dapat digunakan tanpa batasan"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Sentuh untuk mempelajari lebih lanjut."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> dinonaktifkan"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Konferensi Telepon"</string>
</resources>
diff --git a/core/res/res/values-is-rIS-watch/styles_material.xml b/core/res/res/values-is-rIS-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-is-rIS-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index b575e79..a897ba8 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Rétt!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Reyndu aftur"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Reyndu aftur"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Taktu úr lás til að sjá alla eiginleika og gögn"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Hámarksfjölda tilrauna til að opna með andliti náð"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Ekkert SIM-kort"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Ekkert SIM-kort í spjaldtölvunni."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android er að uppfæra…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android er að ræsast…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Fínstillir geymslu."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android er að uppfæra"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Lýkur við Android uppfærslu…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Hugsanlega virka sum forrit ekki fyrr en uppfærslunni lýkur"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> uppfærir…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Fínstillir forrit <xliff:g id="NUMBER_0">%1$d</xliff:g> af <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Undirbýr <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Ræsir forrit."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Losa"</string>
<string name="app_info" msgid="6856026610594615344">"Forritsupplýsingar"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Endurstilla tækið?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Ýttu til að endurstilla tækið"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Byrjar kynningu…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Endurstillir tækið…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Endurstilla tækið?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Þú glatar öllum breytingum og kynningin byrjar aftur eftir <xliff:g id="TIMEOUT">%1$s</xliff:g> sekúndur…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Hætta við"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Endurstilla núna"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Núllstilltu til að nota þetta tæki án takmarkana"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Snertu til að fá frekari upplýsingar."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Slökkt <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Símafundur"</string>
</resources>
diff --git a/core/res/res/values-it-watch/styles_material.xml b/core/res/res/values-it-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-it-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index bfadb9d..9cd74dc 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -310,7 +310,7 @@
<string name="permdesc_enableCarMode" msgid="4853187425751419467">"Consente all\'applicazione di abilitare la modalità automobile."</string>
<string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"chiusura altre applicazioni"</string>
<string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"Consente all\'applicazione di terminare i processi in background di altre applicazioni. Ciò potrebbe causare l\'interruzione di altre applicazioni."</string>
- <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"posizionamento davanti ad altre app"</string>
+ <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"visualizzazione sopra altre app"</string>
<string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Consente all\'applicazione di spostarsi sopra ad altre applicazioni o parti dell\'interfaccia utente. Potrebbe interferire con il tuo utilizzo dell\'interfaccia in qualsiasi applicazione o cambiare ciò che credi di vedere in altre applicazioni."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"esecuzione permanente delle applicazioni"</string>
<string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Consente all\'applicazione di rendere persistenti in memoria alcune sue parti. Ciò può limitare la memoria disponibile per altre applicazioni, rallentando il tablet."</string>
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Corretta."</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Riprova"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Riprova"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Sblocca per accedere a funzioni e dati"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Numero massimo di tentativi di Sblocco col sorriso superato"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Nessuna scheda SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Nessuna scheda SIM presente nel tablet."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Aggiornamento di Android..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Avvio di Android…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Ottimizzazione archiviazione."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Aggiornamento di Android in corso"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Completamento aggiornamento Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Alcune app potrebbero non funzionare correttamente fino al completamento dell\'upgrade"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"Upgrade dell\'app <xliff:g id="APPLICATION">%1$s</xliff:g>…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Ottimizzazione applicazione <xliff:g id="NUMBER_0">%1$d</xliff:g> di <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> in preparazione."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Avvio applicazioni."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Sblocca"</string>
<string name="app_info" msgid="6856026610594615344">"Informazioni app"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Ripristinare il dispositivo?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Tocca per ripristinare il dispositivo"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Avvio della demo…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Ripristino del dispositivo…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Ripristinare il dispositivo?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Perderai tutte le modifiche e la demo verrà riavviata tra <xliff:g id="TIMEOUT">%1$s</xliff:g> secondi…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Annulla"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Ripristina ora"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Esegui il ripristino dei dati di fabbrica per utilizzare il dispositivo senza limitazioni"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Tocca per ulteriori informazioni."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Widget <xliff:g id="LABEL">%1$s</xliff:g> disattivato"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Audioconferenza"</string>
</resources>
diff --git a/core/res/res/values-iw-watch/styles_material.xml b/core/res/res/values-iw-watch/styles_material.xml
new file mode 100644
index 0000000..f44b272
--- /dev/null
+++ b/core/res/res/values-iw-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"מועמדים"</font></string>
+</resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index c2ba6f3..bebe027 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -684,6 +684,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"נכון!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"נסה שוב"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"נסה שוב"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"בטל את הנעילה לכל התכונות והנתונים"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"חרגת ממספר הניסיונות המרבי של זיהוי פנים"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"אין כרטיס SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"אין כרטיס SIM בטאבלט."</string>
@@ -1068,8 +1069,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android מבצע שדרוג…"</string>
<string name="android_start_title" msgid="8418054686415318207">"הפעלת Android מתחילה…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"מתבצעת אופטימיזציה של האחסון."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android מבצע שדרוג"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"מסיים עדכון Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"ייתכן שאפליקציות מסוימות לא יפעלו כראוי עד סיום השדרוג"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> מבצעת שדרוג…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"מבצע אופטימיזציה של אפליקציה <xliff:g id="NUMBER_0">%1$d</xliff:g> מתוך <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"מכין את <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"מפעיל אפליקציות."</string>
@@ -1723,7 +1725,16 @@
<string name="unpin_target" msgid="3556545602439143442">"בטל הצמדה"</string>
<string name="app_info" msgid="6856026610594615344">"פרטי אפליקציה"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"האם לאפס את המכשיר?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"הקש כדי לאפס את המכשיר"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"מתחיל בהדגמה…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"מאפס את המכשיר…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"האם לאפס את המכשיר?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"תאבד את כל השינויים וההדגמה תתחיל שוב בעוד <xliff:g id="TIMEOUT">%1$s</xliff:g> שניות…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"בטל"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"אפס עכשיו"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"איפוס להגדרות היצרן כדי לאפשר שימוש במכשיר ללא מגבלות"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"גע לקבלת מידע נוסף."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> הושבת"</string>
+ <string name="conference_call" msgid="3751093130790472426">"שיחת ועידה"</string>
</resources>
diff --git a/core/res/res/values-ja-watch/styles_material.xml b/core/res/res/values-ja-watch/styles_material.xml
new file mode 100644
index 0000000..7712090
--- /dev/null
+++ b/core/res/res/values-ja-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"候補"</font></string>
+</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index c7c404e..b2a7bd2 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"一致しました"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"もう一度お試しください"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"もう一度お試しください"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"すべての機能とデータを利用するにはロック解除"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"フェイスアンロックの最大試行回数を超えました"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"SIMカードが挿入されていません"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"タブレット内にSIMカードがありません。"</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Androidをアップグレードしています..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Androidの起動中…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"ストレージを最適化しています。"</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android のアップグレード中"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android の更新の終了中…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"アップグレードが完了するまで一部のアプリが正常に動作しない可能性があります"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"「<xliff:g id="APPLICATION">%1$s</xliff:g>」をアップグレードしています…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g>個中<xliff:g id="NUMBER_0">%1$d</xliff:g>個のアプリを最適化しています。"</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g>をペア設定しています。"</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"アプリを起動しています。"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"固定を解除"</string>
<string name="app_info" msgid="6856026610594615344">"アプリ情報"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"端末をリセットしますか?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"端末をリセットするにはタップしてください"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"デモを開始しています…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"端末をリセットしています…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"端末をリセットしますか?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"変更が失われ、<xliff:g id="TIMEOUT">%1$s</xliff:g> 秒後にデモがもう一度開始されます…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"キャンセル"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"今すぐリセット"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"制限なしでこの端末を使用するには初期状態にリセットしてください"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"タップして詳細をご確認ください。"</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"停止済みの「<xliff:g id="LABEL">%1$s</xliff:g>」"</string>
+ <string name="conference_call" msgid="3751093130790472426">"グループ通話"</string>
</resources>
diff --git a/core/res/res/values-ka-rGE-watch/styles_material.xml b/core/res/res/values-ka-rGE-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-ka-rGE-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index 0d27ea5..0295ea5 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"სწორია!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"კიდევ სცადეთ"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"კიდევ სცადეთ"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"ყველა ფუნქციისა და მონაცემის განბლოკვა"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"სახის ამოცნობით განბლოკვის მცდელობამ დაშვებულ რაოდენობას გადააჭარბა"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"SIM ბარათი არ არის"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"ტაბლეტში არ დევს SIM ბარათი."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android ახალ ვერსიაზე გადადის…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android იწყება…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"მეხსიერების ოპტიმიზირება."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android ახალ ვერსიაზე გადადის"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android-ის განახლება სრულდება…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"ახალ ვერსიაზე გადასვლის დასრულებამდე, ზოგიერთმა აპმა შეიძლება არასწორად იმუშაოს"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> ახალ ვერსიაზე გადადის…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"მიმდინარეობს აპლიკაციების ოპტიმიზაცია. დასრულებულია <xliff:g id="NUMBER_0">%1$d</xliff:g>, სულ <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"ემზადება <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"აპების ჩართვა"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"ჩამაგრების მოხსნა"</string>
<string name="app_info" msgid="6856026610594615344">"აპის შესახებ"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"გსურთ მოწყობილობის გადაყენება?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"შეეხეთ მოწყობილობის გადასაყენებლად"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"მიმდინარეობს დემონსტრაციის დაწყება…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"მიმდინარეობს მოწყობილობის გადაყენება…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"გსურთ მოწყობილობის გადაყენება?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"შეტანილი ცვლილებები დაიკარგება, ხოლო დემონსტრაცია ხელახლა <xliff:g id="TIMEOUT">%1$s</xliff:g> წამში დაიწყება…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"გაუქმება"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"ახლავე გადაყენება"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"ამ მოწყობილობის შეზღუდვების გარეშე გამოსაყენებლად, დააბრუნეთ ქარხნული პარამეტრები"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"შეეხეთ მეტის გასაგებად."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"გათიშული <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"საკონფერენციო ზარი"</string>
</resources>
diff --git a/core/res/res/values-kk-rKZ-watch/styles_material.xml b/core/res/res/values-kk-rKZ-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-kk-rKZ-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index c975675..b5931c1 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Дұрыс!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Қайталап көріңіз"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Қайталап көріңіз"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Мүмкіндіктер мен деректер үшін құлыпты ашыңыз"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Бет-әлпет арқылы ашу әрекеттері анықталған шегінен асып кетті"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"SIM картасы жоқ"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Планшетте SIM картасы жоқ."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android жаңартылуда…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android іске қосылуда…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Қойманы оңтайландыру."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android жаңартылуда"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android жүйесін жаңарту аяқталуда…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Жаңарту аяқталғанға дейін кейбір қолданбалар дұрыс жұмыс істемеуі мүмкін"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> жаңартылуда…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> ішінен <xliff:g id="NUMBER_0">%1$d</xliff:g> қолданба оңтайландырылуда."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> дайындалуда."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Қолданбалар іске қосылуда."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Босату"</string>
<string name="app_info" msgid="6856026610594615344">"Қолданба ақпараты"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Құрылғыны бастапқы күйге қайтару керек пе?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Құрылғыны бастапқы күйге келтіру үшін түртіңіз"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Демо нұсқасы іске қосылуда..."</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Құрылғы бастапқы күйге қайтарылуда..."</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Құрылғыны басқапқы күйге қайтару керек пе?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Барлық өзгеріс жоғалады және демо нұсқасы <xliff:g id="TIMEOUT">%1$s</xliff:g> секундтан кейін қайта қосылады…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Бас тарту"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Қазір бастапқы күйге қайтару"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Осы құрылғыны шектеусіз пайдалану үшін зауыттық параметрлерді қалпына келтіріңіз"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Қосымша мәліметтер алу үшін түртіңіз."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> өшірулі"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Конференциялық қоңырау"</string>
</resources>
diff --git a/core/res/res/values-km-rKH-watch/styles_material.xml b/core/res/res/values-km-rKH-watch/styles_material.xml
new file mode 100644
index 0000000..e54cb00
--- /dev/null
+++ b/core/res/res/values-km-rKH-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"បេក្ខជន"</font></string>
+</resources>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 0c99e16..71f8cba 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"ត្រឹមត្រូវ!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"ព្យាយាមម្ដងទៀត"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"ព្យាយាមម្ដងទៀត"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"ដោះសោលក្ខណៈពិសេស និងទិន្នន័យទាំងអស់"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"បានលើសការព្យាយាមដោះសោតាមទម្រង់មុខ"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"គ្មានស៊ីមកាត"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"គ្មានស៊ីមកាតក្នុងកុំព្យូទ័របន្ទះ។"</string>
@@ -1024,8 +1025,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android កំពុងធ្វើបច្ចុប្បន្នភាព..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android កំពុងចាប់ផ្ដើម…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"កំពុងធ្វើឲ្យឧបករណ៍ផ្ទុកមានប្រសិទ្ធភាព។"</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android កំពុងអាប់គ្រេត..."</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"កំពុងបញ្ចប់ការអាប់ដេត Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"កម្មវិធីមួយចំនួនអាចនឹងមិនដំណើរការប្រក្រតីនោះទេ រហូតដល់ការអាប់គ្រេតបញ្ចប់"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> អាប់គ្រេត…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"ធ្វើឲ្យកម្មវិធីប្រសើរឡើង <xliff:g id="NUMBER_0">%1$d</xliff:g> នៃ <xliff:g id="NUMBER_1">%2$d</xliff:g> ។"</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"កំពុងរៀបចំ <xliff:g id="APPNAME">%1$s</xliff:g>។"</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"ចាប់ផ្ដើមកម្មវិធី។"</string>
@@ -1653,7 +1655,16 @@
<string name="unpin_target" msgid="3556545602439143442">"មិនខ្ទាស់"</string>
<string name="app_info" msgid="6856026610594615344">"ព័ត៌មានកម្មវិធី"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"កំណត់ឧបករណ៍ឡើងវិញឬ?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"ប៉ះដើម្បីកំណត់ឧបករណ៍ឡើងវិញ"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"កំពុងចាប់ផ្តើមការបង្ហាញសាកល្បង…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"កំពុងកំណត់ឧបករណ៍ឡើងវិញ…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"កំណត់ឧបករណ៍ឡើងវិញឬ?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"អ្នកនឹងបាត់បង់ការផ្លាស់ប្តូរណាមួយ ហើយការបង្ហាញសាកល្បងនឹងចាប់ផ្តើមម្តងទៀតក្នុងរយៈពេល <xliff:g id="TIMEOUT">%1$s</xliff:g> វិនាទីទៀត…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"បោះបង់"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"កំណត់ឡើងវិញឥឡូវនេះ"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"កំណត់ដូចចេញពីរោងចក្រឡើងវិញដើម្បីប្រើឧបករណ៍នេះដោយគ្មានការរឹតបន្តឹង"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"ប៉ះ ដើម្បីស្វែងយល់បន្ថែម។"</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> ដែលបានបិទដំណើរការ"</string>
+ <string name="conference_call" msgid="3751093130790472426">"ការហៅជាក្រុម"</string>
</resources>
diff --git a/core/res/res/values-kn-rIN-watch/styles_material.xml b/core/res/res/values-kn-rIN-watch/styles_material.xml
new file mode 100644
index 0000000..1ec23a4
--- /dev/null
+++ b/core/res/res/values-kn-rIN-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"ಅಭ್ಯರ್ಥಿಗಳು"</font></string>
+</resources>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index c172e91..770af19 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"ಸರಿಯಾಗಿದೆ!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"ಎಲ್ಲ ವೈಶಿಷ್ಟ್ಯಗಳು ಮತ್ತು ಡೇಟಾಗೆ ಅನ್ಲಾಕ್ ಮಾಡಿ"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"ಗರಿಷ್ಠ ಫೇಸ್ ಅನ್ಲಾಕ್ ಪ್ರಯತ್ನಗಳು ಮೀರಿವೆ"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"ಯಾವುದೇ ಸಿಮ್ ಕಾರ್ಡ್ ಇಲ್ಲ"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"ಟ್ಯಾಬ್ಲೆಟ್ನಲ್ಲಿ ಸಿಮ್ ಕಾರ್ಡ್ ಇಲ್ಲ."</string>
@@ -969,9 +970,9 @@
<string name="whichViewApplication" msgid="3272778576700572102">"ಇದರ ಮೂಲಕ ತೆರೆಯಿರಿ"</string>
<string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s ಜೊತೆಗೆ ತೆರೆಯಿರಿ"</string>
<string name="whichViewApplicationLabel" msgid="2666774233008808473">"ತೆರೆ"</string>
- <string name="whichEditApplication" msgid="144727838241402655">"ಇವರ ಜೊತೆಗೆ ಸಂಪಾದಿಸಿ"</string>
- <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$s ಜೊತೆಗೆ ಸಂಪಾದಿಸಿ"</string>
- <string name="whichEditApplicationLabel" msgid="7183524181625290300">"ಸಂಪಾದಿಸು"</string>
+ <string name="whichEditApplication" msgid="144727838241402655">"ಇವರ ಜೊತೆಗೆ ಎಡಿಟ್ ಮಾಡಿ"</string>
+ <string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$s ಜೊತೆಗೆ ಎಡಿಟ್ ಮಾಡಿ"</string>
+ <string name="whichEditApplicationLabel" msgid="7183524181625290300">"ಎಡಿಟ್"</string>
<string name="whichSendApplication" msgid="6902512414057341668">"ಹಂಚಿಕೊಳ್ಳಿ"</string>
<string name="whichSendApplicationNamed" msgid="2799370240005424391">"%1$s ಜೊತೆಗೆ ಹಂಚಿಕೊಳ್ಳಿ"</string>
<string name="whichSendApplicationLabel" msgid="4579076294675975354">"ಹಂಚಿಕೊಳ್ಳಿ"</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android ಅಪ್ಗ್ರೇಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"ಸಂಗ್ರಹಣೆಯನ್ನು ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗುತ್ತಿದೆ."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android ಅಪ್ಗ್ರೇಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android ಅಪ್ಡೇಟ್ ಮುಗಿಸಲಾಗುತ್ತಿದೆ…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"ಅಪ್ಗ್ರೇಡ್ ಮುಗಿಯುವ ತನಕ ಕೆಲವು ಅಪ್ಲಿಕೇಶನ್ಗಳು ಸರಿಯಾಗಿ ಕೆಲಸ ಮಾಡದಿರಬಹುದು"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> ಅಪ್ಗ್ರೇಡ್ ಆಗುತ್ತಿದೆ..."</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> ರಲ್ಲಿ <xliff:g id="NUMBER_0">%1$d</xliff:g> ಅಪ್ಲಿಕೇಶನ್ ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗುತ್ತಿದೆ."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> ಸಿದ್ಧಪಡಿಸಲಾಗುತ್ತಿದೆ."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ."</string>
@@ -1314,7 +1316,7 @@
<string name="storage_usb_drive" msgid="6261899683292244209">"USB ಡ್ರೈವ್"</string>
<string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB ಡ್ರೈವ್"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB ಸಂಗ್ರಹಣೆ"</string>
- <string name="extract_edit_menu_button" msgid="8940478730496610137">"ಸಂಪಾದಿಸು"</string>
+ <string name="extract_edit_menu_button" msgid="8940478730496610137">"ಎಡಿಟ್"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"ಡೇಟಾ ಬಳಕೆಯ ಎಚ್ಚರಿಕೆ"</string>
<string name="data_usage_warning_body" msgid="6660692274311972007">"ಬಳಕೆ ಮತ್ತು ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ವೀಕ್ಷಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G ಡೇಟಾ ಮೀತಿಯನ್ನು ತಲುಪಿದೆ"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"ಅನ್ಪಿನ್"</string>
<string name="app_info" msgid="6856026610594615344">"ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"ಸಾಧನವನ್ನು ಮರುಹೊಂದಿಸುವುದೇ?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"ಸಾಧನ ಮರುಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"ಡೆಮೋ ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ..."</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"ಸಾಧನ ಮರುಹೊಂದಿಸಲಾಗುತ್ತಿದೆ..."</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"ಸಾಧನವನ್ನು ಮರುಹೊಂದಿಸುವುದೇ?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"ನೀವು ಯಾವುದೇ ಬದಲಾವಣೆಗಳನ್ನು ಕಳೆದುಕೊಳ್ಳುತ್ತೀರಿ ಮತ್ತು <xliff:g id="TIMEOUT">%1$s</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಡೆಮೋ ಮತ್ತೆ ಪ್ರಾರಂಭವಾಗುತ್ತದೆ..."</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"ರದ್ದುಮಾಡು"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"ಈಗಲೇ ಮರುಹೊಂದಿಸು"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"ನಿರ್ಬಂಧಗಳು ಇಲ್ಲದೆಯೇ ಈ ಸಾಧನವನ್ನು ಬಳಸಲು ಫ್ಯಾಕ್ಟರಿ ಮರುಹೊಂದಿಸಿ"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"ಇನ್ನಷ್ಟು ತಿಳಿಯಲು ಸ್ಪರ್ಶಿಸಿ."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="conference_call" msgid="3751093130790472426">"ಕಾನ್ಫರೆನ್ಸ್ ಕರೆ"</string>
</resources>
diff --git a/core/res/res/values-ko-watch/styles_material.xml b/core/res/res/values-ko-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-ko-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 254adc82..5b446b4 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -245,17 +245,17 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"주소록"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"주소록에 접근할 수 있도록"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"위치"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"이 기기의 위치정보에 접근할 수 있도록"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"이 기기의 위치정보에 액세스"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"캘린더"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"일정에 접근할 수 있도록"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"문자 메시지를 보내고 확인할 수 있도록"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"저장"</string>
- <string name="permgroupdesc_storage" msgid="637758554581589203">"기기 사진, 미디어, 파일에 접근할 수 있도록"</string>
+ <string name="permgroupdesc_storage" msgid="637758554581589203">"기기 사진, 미디어, 파일 액세스"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"마이크"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"오디오를 녹음할 수 있도록"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"카메라"</string>
- <string name="permgroupdesc_camera" msgid="3250611594678347720">"사진 및 동영상을 촬영할 수 있도록"</string>
+ <string name="permgroupdesc_camera" msgid="3250611594678347720">"사진 및 동영상 촬영"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"전화"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"통화 상태를 관리하거나 전화를 걸 수 있도록"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"신체 센서"</string>
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"맞습니다."</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"다시 시도"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"다시 시도"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"모든 기능 및 데이터 잠금 해제"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"얼굴 인식 잠금해제 최대 시도 횟수를 초과했습니다."</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"SIM 카드가 없습니다."</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"태블릿에 SIM 카드가 없습니다."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android 업그레이드 중.."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android가 시작되는 중…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"저장소 최적화 중"</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android 업그레이드 중"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android 업데이트 완료 중…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"특정 앱은 업그레이드가 완료될 때까지 제대로 작동하지 않을 수 있습니다."</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> 업그레이드 중…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"앱 <xliff:g id="NUMBER_1">%2$d</xliff:g>개 중 <xliff:g id="NUMBER_0">%1$d</xliff:g>개 최적화 중"</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> 준비 중..."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"앱을 시작하는 중입니다."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"고정 해제"</string>
<string name="app_info" msgid="6856026610594615344">"앱 정보"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"기기를 초기화하시겠습니까?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"기기를 초기화하려면 탭하세요."</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"데모 시작 중..."</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"기기 초기화 중..."</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"기기를 초기화하시겠습니까?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"변경사항이 사라지며 데모가 <xliff:g id="TIMEOUT">%1$s</xliff:g>초 후에 시작됩니다."</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"취소"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"지금 초기화"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"제한 없이 기기를 사용하기 위한 초기화"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"자세한 내용을 보려면 터치하세요."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> 사용 중지됨"</string>
+ <string name="conference_call" msgid="3751093130790472426">"다자간 통화"</string>
</resources>
diff --git a/core/res/res/values-ky-rKG-watch/styles_material.xml b/core/res/res/values-ky-rKG-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-ky-rKG-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index 50340d4..0f82dcb 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Туура!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Дагы аракет кылыңыз"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Дагы аракет кылыңыз"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Элементтердин жана дайындардын кулпусун ачуу"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Жүзүнөн таанып ачуу аракеттеринин чегинен аштыңыз"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"SIM-карта жок"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Планшетте SIM-карта жок."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android жаңыртылууда…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android жүргүзүлүүдө…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Сактагыч ыңгайлаштырылууда."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android жаңыртылууда"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android\'ди жаңыртуу аякталууда..."</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Жаңыртуу аягына чыкмайынча айрым колдонмолор талаптагыдай иштебей калышы мүмкүн"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> жаңыртылууда..."</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> ичинен <xliff:g id="NUMBER_0">%1$d</xliff:g> колдонмо ыңгайлаштырылууда."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> даярдалууда."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Колдонмолорду иштетип баштоо"</string>
@@ -1654,7 +1656,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Кадоодон алып коюу"</string>
<string name="app_info" msgid="6856026610594615344">"Колдонмо тууралуу"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Түзмөк баштапкы абалга келтирилсинби?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Түзмөктү баштапкы абалга келтирүү үчүн таптап коюңуз"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Демо режим башталууда…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Түзмөк баштапкы абалга келтирилүүдө…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Түзмөк баштапкы абалга келтирилсинби?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Бардык өзгөртүүлөр жоголуп, демо режим <xliff:g id="TIMEOUT">%1$s</xliff:g> секунддан кийин кайра башталат…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Жокко чыгаруу"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Баштапкы абалга келтирүү"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Бул түзмөктү чектөөсүз колдонуу үчүн аны баштапкы абалга келтириңиз"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Көбүрөөк билүү үчүн тийип коюңуз."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> өчүрүлдү"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Конференц чалуу"</string>
</resources>
diff --git a/core/res/res/values-land/integers.xml b/core/res/res/values-land/integers.xml
index 020fd23..a578989 100644
--- a/core/res/res/values-land/integers.xml
+++ b/core/res/res/values-land/integers.xml
@@ -23,4 +23,6 @@
<integer name="kg_widget_region_weight">45</integer>
<integer name="kg_security_flipper_weight">55</integer>
<integer name="kg_glowpad_rotation_offset">-90</integer>
+
+ <integer name="date_picker_header_max_lines_material">4</integer>
</resources>
diff --git a/core/res/res/values-lo-rLA-watch/styles_material.xml b/core/res/res/values-lo-rLA-watch/styles_material.xml
new file mode 100644
index 0000000..1f845b9
--- /dev/null
+++ b/core/res/res/values-lo-rLA-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"ແຄນດິເດດ"</font></string>
+</resources>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index 1ef7622..e2720c7 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"ຖືກຕ້ອງ!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"ລອງໃໝ່ອີກຄັ້ງ"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"ທົດລອງອີກຄັ້ງ"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"ປົດລັອກຄຸນສົມບັດ ແລະ ຂໍ້ມູນທັງໝົດ"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"ຄວາມພະຍາຍາມປົດລັອກດ້ວຍໜ້ານັ້ນ ເກີນຈຳນວນທີ່ກຳນົດແລ້ວ"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"ບໍ່ມີ SIM card."</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"ບໍ່ມີຊິມກາດໃນແທັບເລັດ."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"ກຳລັງອັບເກຣດ Android..."</string>
<string name="android_start_title" msgid="8418054686415318207">"ກຳລັງເລີ່ມລະບົບ Android …"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"ການປັບບ່ອນເກັບຂໍ້ມູນໃຫ້ເໝາະສົມ."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"ກຳລັງອັບເກຣດ Android"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"ກຳລັງສຳເລັດຂັ້ນຕອນການອັບເດດ Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"ບາງແອັບອາດບໍ່ສາມາດເຮັດວຽກໄດ້ປົກກະຕິຈົນກວ່າຈະອັບເກຣດສຳເລັດ"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"ກຳລັງອັບເກຣດ<xliff:g id="APPLICATION">%1$s</xliff:g>…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"ກຳລັງປັບປຸງປະສິດທິພາບແອັບຯທີ <xliff:g id="NUMBER_0">%1$d</xliff:g> ຈາກທັງໝົດ <xliff:g id="NUMBER_1">%2$d</xliff:g> ແອັບຯ."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"ກຳລັງກຽມ <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"ກຳລັງເປີດແອັບຯ."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"ຖອນປັກໝຸດ"</string>
<string name="app_info" msgid="6856026610594615344">"ຂໍ້ມູນແອັບ"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"ຣີເຊັດອຸປະກອນບໍ?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"ແຕະເພື່ອຣີເຊັດອຸປະກອນ"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"ກຳລັງເລີ່ມເດໂມ…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"ກຳລັງຣີເຊັດອຸປະກອນ…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"ຣີເຊັດອຸປະກອນບໍ?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"ທ່ານຈະສູນເສຍການປ່ຽນແປງ ແລະ ເດໂມຈະເລີ່ມອີກຄັ້ງໃນອີກ <xliff:g id="TIMEOUT">%1$s</xliff:g> ວິນາທີ…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"ຍົກເລີກ"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"ຣີເຊັດດຽວນີ້"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"ຣີເຊັດໃຫ້ເປັນຄ່າໂຮງງານເພື່ອໃຊ້ອຸປະກອນນີ້ໂດຍບໍ່ມີຂໍ້ຈຳກັດ"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"ແຕະເພື່ອສຶກສາເພີ່ມເຕີມ."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"ປິດການນຳໃຊ້ <xliff:g id="LABEL">%1$s</xliff:g> ແລ້ວ"</string>
+ <string name="conference_call" msgid="3751093130790472426">"ການປະຊຸມສາຍ"</string>
</resources>
diff --git a/core/res/res/values-lt-watch/styles_material.xml b/core/res/res/values-lt-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-lt-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 80ff67e..ab38c0c 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -684,6 +684,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Teisingai!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Bandykite dar kartą"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Bandykite dar kartą"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Atrakinę pasieksite visas funkcijas ir duomenis"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Viršijote maksimalų atrakinimo pagal veidą bandymų skaičių"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Nėra SIM kortelės"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Planšetiniame kompiuteryje nėra SIM kortelės."</string>
@@ -1068,8 +1069,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"„Android“ naujovinama..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Paleidžiama „Android“…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimizuojama saugykla."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"„Android“ naujovinama"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Baigiamas „Android“ atnauj. procesas…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Kai kurios programos gali tinkamai neveikti, kol naujovinimo procesas nebus baigtas"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"„<xliff:g id="APPLICATION">%1$s</xliff:g>“ naujovinama..."</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optimizuojama <xliff:g id="NUMBER_0">%1$d</xliff:g> progr. iš <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Ruošiama „<xliff:g id="APPNAME">%1$s</xliff:g>“."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Paleidžiamos programos."</string>
@@ -1723,7 +1725,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Atsegti"</string>
<string name="app_info" msgid="6856026610594615344">"Programos informacija"</string>
<string name="negative_duration" msgid="5688706061127375131">"–<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Iš naujo nustatyti įrenginį?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Palieskite, kad iš naujo nustatytumėte įrenginį"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Paleidžiama demonstracinė versija…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Įrenginys nustatomas iš naujo…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Iš naujo nustatyti įrenginį?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Prarasite visus pakeitimus, o demonstracinė versija bus paleista iš naujo po <xliff:g id="TIMEOUT">%1$s</xliff:g> sek…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Atšaukti"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Nustatyti iš naujo dabar"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Atkurkite gamyklinius nustatymus, kad galėtumėte naudoti šį įrenginį be apribojimų"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Palieskite, kad sužinotumėte daugiau."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Išj. valdiklis „<xliff:g id="LABEL">%1$s</xliff:g>“"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Konferencinis skambutis"</string>
</resources>
diff --git a/core/res/res/values-lv-watch/styles_material.xml b/core/res/res/values-lv-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-lv-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 210d05b..35b699f 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -681,6 +681,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Pareizi!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Mēģināt vēlreiz"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Mēģināt vēlreiz"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Atbloķēt visām funkcijām un datiem"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Ir pārsniegts maksimālais Autorizācijas pēc sejas mēģinājumu skaits."</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Nav SIM kartes"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Planšetdatorā nav SIM kartes."</string>
@@ -1045,8 +1046,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Notiek Android jaunināšana..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Notiek Android palaišana…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Notiek krātuves optimizēšana."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Notiek Android jaunināšana..."</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Tiek pabeigta Android atjaunināšana…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Kamēr jaunināšana nebūs pabeigta, dažas lietotnes, iespējams, nedarbosies pareizi."</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"Notiek lietotnes <xliff:g id="APPLICATION">%1$s</xliff:g> jaunināšana…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Tiek optimizēta <xliff:g id="NUMBER_0">%1$d</xliff:g>. lietotne no <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Notiek lietotnes <xliff:g id="APPNAME">%1$s</xliff:g> sagatavošana."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Notiek lietotņu palaišana."</string>
@@ -1687,7 +1689,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Atspraust"</string>
<string name="app_info" msgid="6856026610594615344">"Lietotnes informācija"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Vai atiestatīt ierīci?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Pieskarieties, lai atiestatītu ierīci"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Notiek demonstrācijas palaišana..."</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Notiek ierīces atiestatīšana..."</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Vai atiestatīt ierīci?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Pēc <xliff:g id="TIMEOUT">%1$s</xliff:g> sekundēm zaudēsiet visas izmaiņas un tiks atkārtoti palaista demonstrācija..."</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Atcelt"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Atiestatīt tūlīt"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Rūpnīcas datu atiestatīšana ierīces neierobežotai izmantošanai"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Pieskarieties, lai uzzinātu vairāk."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> atspējots"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Konferences zvans"</string>
</resources>
diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml
index 39ea2bf..4e17e98 100755
--- a/core/res/res/values-mcc311-mnc480/config.xml
+++ b/core/res/res/values-mcc311-mnc480/config.xml
@@ -69,4 +69,12 @@
<item>true</item>
</string-array>
<string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true</string>
+
+ <!-- Network switching warnings use toasts. -->
+ <integer translatable="false" name="config_networkNotifySwitchType">2</integer>
+ <!-- Notify when switching from Wi-Fi to cellular data. -->
+ <string-array translatable="false" name="config_networkNotifySwitches">
+ <item>WIFI-CELLULAR</item>
+ </string-array>
+
</resources>
diff --git a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl b/core/res/res/values-mcc466-mnc01/config.xml
similarity index 74%
copy from core/java/com/android/internal/app/EphemeralResolveInfo.aidl
copy to core/res/res/values-mcc466-mnc01/config.xml
index 529527b..3c379d14 100644
--- a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl
+++ b/core/res/res/values-mcc466-mnc01/config.xml
@@ -1,5 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
/*
-** Copyright 2015, The Android Open Source Project
+** 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.
@@ -13,7 +15,7 @@
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-
-package com.android.internal.app;
-
-parcelable EphemeralResolveInfo;
+-->
+<resources>
+ <bool name="config_use_sim_language_file">false</bool>
+</resources>
diff --git a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl b/core/res/res/values-mcc466-mnc02/config.xml
similarity index 74%
copy from core/java/com/android/internal/app/EphemeralResolveInfo.aidl
copy to core/res/res/values-mcc466-mnc02/config.xml
index 529527b..3c379d14 100644
--- a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl
+++ b/core/res/res/values-mcc466-mnc02/config.xml
@@ -1,5 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
/*
-** Copyright 2015, The Android Open Source Project
+** 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.
@@ -13,7 +15,7 @@
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-
-package com.android.internal.app;
-
-parcelable EphemeralResolveInfo;
+-->
+<resources>
+ <bool name="config_use_sim_language_file">false</bool>
+</resources>
diff --git a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl b/core/res/res/values-mcc466-mnc03/config.xml
similarity index 74%
copy from core/java/com/android/internal/app/EphemeralResolveInfo.aidl
copy to core/res/res/values-mcc466-mnc03/config.xml
index 529527b..3c379d14 100644
--- a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl
+++ b/core/res/res/values-mcc466-mnc03/config.xml
@@ -1,5 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
/*
-** Copyright 2015, The Android Open Source Project
+** 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.
@@ -13,7 +15,7 @@
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-
-package com.android.internal.app;
-
-parcelable EphemeralResolveInfo;
+-->
+<resources>
+ <bool name="config_use_sim_language_file">false</bool>
+</resources>
diff --git a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl b/core/res/res/values-mcc466-mnc06/config.xml
similarity index 74%
copy from core/java/com/android/internal/app/EphemeralResolveInfo.aidl
copy to core/res/res/values-mcc466-mnc06/config.xml
index 529527b..3c379d14 100644
--- a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl
+++ b/core/res/res/values-mcc466-mnc06/config.xml
@@ -1,5 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
/*
-** Copyright 2015, The Android Open Source Project
+** 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.
@@ -13,7 +15,7 @@
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-
-package com.android.internal.app;
-
-parcelable EphemeralResolveInfo;
+-->
+<resources>
+ <bool name="config_use_sim_language_file">false</bool>
+</resources>
diff --git a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl b/core/res/res/values-mcc466-mnc07/config.xml
similarity index 74%
copy from core/java/com/android/internal/app/EphemeralResolveInfo.aidl
copy to core/res/res/values-mcc466-mnc07/config.xml
index 529527b..3c379d14 100644
--- a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl
+++ b/core/res/res/values-mcc466-mnc07/config.xml
@@ -1,5 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
/*
-** Copyright 2015, The Android Open Source Project
+** 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.
@@ -13,7 +15,7 @@
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-
-package com.android.internal.app;
-
-parcelable EphemeralResolveInfo;
+-->
+<resources>
+ <bool name="config_use_sim_language_file">false</bool>
+</resources>
diff --git a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl b/core/res/res/values-mcc466-mnc11/config.xml
similarity index 74%
copy from core/java/com/android/internal/app/EphemeralResolveInfo.aidl
copy to core/res/res/values-mcc466-mnc11/config.xml
index 529527b..3c379d14 100644
--- a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl
+++ b/core/res/res/values-mcc466-mnc11/config.xml
@@ -1,5 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
/*
-** Copyright 2015, The Android Open Source Project
+** 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.
@@ -13,7 +15,7 @@
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-
-package com.android.internal.app;
-
-parcelable EphemeralResolveInfo;
+-->
+<resources>
+ <bool name="config_use_sim_language_file">false</bool>
+</resources>
diff --git a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl b/core/res/res/values-mcc466-mnc92/config.xml
similarity index 74%
copy from core/java/com/android/internal/app/EphemeralResolveInfo.aidl
copy to core/res/res/values-mcc466-mnc92/config.xml
index 529527b..3c379d14 100644
--- a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl
+++ b/core/res/res/values-mcc466-mnc92/config.xml
@@ -1,5 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
/*
-** Copyright 2015, The Android Open Source Project
+** 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.
@@ -13,7 +15,7 @@
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-
-package com.android.internal.app;
-
-parcelable EphemeralResolveInfo;
+-->
+<resources>
+ <bool name="config_use_sim_language_file">false</bool>
+</resources>
diff --git a/core/res/res/values-mk-rMK-watch/styles_material.xml b/core/res/res/values-mk-rMK-watch/styles_material.xml
new file mode 100644
index 0000000..89c3366
--- /dev/null
+++ b/core/res/res/values-mk-rMK-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"кандидати"</font></string>
+</resources>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index a24ad57..e0ec16a 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -254,7 +254,7 @@
<string name="permgroupdesc_storage" msgid="637758554581589203">"пристапува до фотографии, аудио-видео и датотеки на уредот"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Микрофон"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"снима аудио"</string>
- <string name="permgrouplab_camera" msgid="4820372495894586615">"Фотоапарат"</string>
+ <string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string>
<string name="permgroupdesc_camera" msgid="3250611594678347720">"фотографира и снима видео"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Телефон"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"упатува и управува со телефонски повици"</string>
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Точно!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Обидете се повторно"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Обидете се повторно"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Отклучи за сите функции и податоци"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Максималниот број обиди на отклучување со лице е надминат"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Нема СИМ картичка"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Во таблетот нема СИМ картичка."</string>
@@ -740,7 +741,7 @@
<string name="keyguard_accessibility_widget" msgid="6527131039741808240">"Виџет <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
<string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Избирач на корисник"</string>
<string name="keyguard_accessibility_status" msgid="8008264603935930611">"Статус"</string>
- <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Фотоапарат"</string>
+ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"Камера"</string>
<string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Контроли на медиуми"</string>
<string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"Прередувањето виџети започна."</string>
<string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Прередувањето виџети заврши."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android се ажурира…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android стартува…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Оптимизирање на складирањето."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android се ажурира"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Се завршува ажурирањето на Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Некои апликации може да не работат правилно додека не се заврши надградбата"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> се надградува…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Се оптимизира апликација <xliff:g id="NUMBER_0">%1$d</xliff:g> од <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Се подготвува <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Се стартуваат апликациите."</string>
@@ -1653,7 +1655,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Откачете"</string>
<string name="app_info" msgid="6856026610594615344">"Информации за апликација"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Да се ресетира уредот?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Допрете за да го ресетирате уредот"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Се вклучува демонстрацијата…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Се ресетира уредот…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Да се ресетира уредот?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Ќе ги изгубите измените и демонстрацијата ќе започне повторно по <xliff:g id="TIMEOUT">%1$s</xliff:g> секунди…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Откажи"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Ресетирај сега"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Ресетирајте до фабричките поставки за уредов да го користите без ограничувања"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Допрете за да дознаете повеќе."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Оневозможен <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Конференциски повик"</string>
</resources>
diff --git a/core/res/res/values-ml-rIN-watch/styles_material.xml b/core/res/res/values-ml-rIN-watch/styles_material.xml
new file mode 100644
index 0000000..9d38c0d
--- /dev/null
+++ b/core/res/res/values-ml-rIN-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"കാൻഡിഡേറ്റുകൾ"</font></string>
+</resources>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index f91db2d..39d493a 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"ശരി!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"വീണ്ടും ശ്രമിക്കുക"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"വീണ്ടും ശ്രമിക്കുക"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"എല്ലാ ഫീച്ചറുകളും വിവരങ്ങളും ലഭിക്കാൻ അൺലോക്കുചെയ്യുക"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് ശ്രമങ്ങളുടെ പരമാവധി കഴിഞ്ഞു"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"സിം കാർഡില്ല"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"ടാബ്ലെറ്റിൽ സിം കാർഡൊന്നുമില്ല."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android അപ്ഗ്രേഡുചെയ്യുന്നു…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android ആരംഭിക്കുന്നു…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"സ്റ്റോറേജ് ഒപ്റ്റിമൈസ് ചെയ്യുന്നു."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android അപ്ഗ്രേഡുചെയ്യുന്നു"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android അപ്ഡേറ്റ് പൂർത്തിയാക്കുന്നു…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"അപ്ഗ്രേഡ് പൂർത്തിയാകുന്നത് വരെ ചില ആപ്സ് ശരിയായി പ്രവർത്തിച്ചേക്കില്ല"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> അപ്ഗ്രേഡ് ചെയ്യുന്നു…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_0">%1$d</xliff:g> / <xliff:g id="NUMBER_1">%2$d</xliff:g> അപ്ലിക്കേഷൻ ഓപ്റ്റിമൈസ് ചെയ്യുന്നു."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> തയ്യാറാക്കുന്നു."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"അപ്ലിക്കേഷനുകൾ ആരംഭിക്കുന്നു."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"അൺപിൻ ചെയ്യുക"</string>
<string name="app_info" msgid="6856026610594615344">"ആപ്പ് വിവരം"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"ഉപകരണം പുനക്രമീകരിക്കണോ?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"ഉപകരണം പുനക്രമീകരിക്കാൻ ടാപ്പുചെയ്യുക"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"ഡെമോ ആരംഭിക്കുന്നു…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"ഉപകരണം പുനക്രമീകരിക്കുന്നു…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"ഉപകരണം പുനക്രമീകരിക്കണോ?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"മാറ്റങ്ങളെല്ലാം നിങ്ങൾക്ക് നഷ്ടപ്പെടും, <xliff:g id="TIMEOUT">%1$s</xliff:g> സെക്കൻഡിൽ ഡെമോ വീണ്ടും തുടങ്ങും…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"റദ്ദാക്കുക"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"ഇപ്പോൾ പുനക്രമീകരിക്കുക"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"നിയന്ത്രണങ്ങൾ ഇല്ലാതെ ഈ ഉപകരണം ഉപയോഗിക്കാൻ ഫാക്ടറി റീസെറ്റ് നടത്തുക"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"കൂടുതലറിയുന്നതിന് സ്പർശിക്കുക."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> പ്രവർത്തനരഹിതമാക്കി"</string>
+ <string name="conference_call" msgid="3751093130790472426">"കോൺഫറൻസ് കോൾ"</string>
</resources>
diff --git a/core/res/res/values-mn-rMN-watch/styles_material.xml b/core/res/res/values-mn-rMN-watch/styles_material.xml
new file mode 100644
index 0000000..f65ea48
--- /dev/null
+++ b/core/res/res/values-mn-rMN-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"нэр дэвшигч"</font></string>
+</resources>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index f54a485..5f384a1 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Зөв!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Дахин оролдох"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Дахин оролдох"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Бүх онцлог, өгөгдлийн түгжээг тайлах"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Нүүрээр түгжээ тайлах оролдлогын тоо дээд хэмжээнээс хэтэрсэн"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"SIM карт байхгүй"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Таблет SIM картгүй."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Андройдыг дэвшүүлж байна…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Андройд эхэлж байна..."</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Хадгалалтыг сайжруулж байна."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Андройдыг дэвшүүлж байна"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Андройдын шинэчлэлтийг дуусгаж байна…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Шинэчилж дуустал зарим апп хэвийн бус ажиллаж болзошгүй"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g>-г сайжруулж байна…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g>-н <xliff:g id="NUMBER_0">%1$d</xliff:g> апп-г тохируулж байна."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Бэлдэж байна <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Апп-г эхлүүлж байна."</string>
@@ -1649,7 +1651,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Unpin"</string>
<string name="app_info" msgid="6856026610594615344">"Апп-н мэдээлэл"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Төхөөрөмжийг шинэчлэх үү?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Төхөөрөмжийг шинэчлэхийн тулд товшино уу"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Жишээг эхлүүлж байна…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Төхөөрөмжийг шинэчилж байна…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Төхөөрөмжийг шинэчлэх үү?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Таны хийсэн өөрчлөлтийг хадгалахгүй бөгөөд жишээ <xliff:g id="TIMEOUT">%1$s</xliff:g> секундын дотор дахин эхлэх болно..."</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Цуцлах"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Одоо шинэчлэх"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Энэ төхөөрөмжийг хязгаарлалтгүй ашиглахын тулд үйлдвэрийн тохиргоонд дахин тохируулна уу"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Дэлгэрэнгүй үзэх бол дарна уу."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g>-г цуцалсан"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Хурлын дуудлага"</string>
</resources>
diff --git a/core/res/res/values-mr-rIN-watch/styles_material.xml b/core/res/res/values-mr-rIN-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-mr-rIN-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index c7d2191..36d6f6a 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"अचूक!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"पुन्हा प्रयत्न करा"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"पुन्हा प्रयत्न करा"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"सर्व वैशिष्ट्ये आणि डेटासाठी अनलॉक करा"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"कमाल चेहरा अनलॉक प्रयत्न ओलांडले"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"सिम कार्ड नाही"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"टॅब्लेटमध्ये सिम कार्ड नाही."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android श्रेणीसुधारित होत आहे..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android प्रारंभ करत आहे…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"संचयन ऑप्टिमाइझ करत आहे."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android श्रेणीसुधारित होत आहे"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android अद्यतन समाप्त करीत आहे..."</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"श्रेणीसुधारणा पूर्ण होईपर्यंत काही अॅप्स योग्यरित्या कार्य करणार नाहीत"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> श्रेणीसुधारित करीत आहे…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> पैकी <xliff:g id="NUMBER_0">%1$d</xliff:g> अॅप ऑप्टिमाइझ करत आहे."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> तयार करीत आहे."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"अॅप्स प्रारंभ करत आहे."</string>
@@ -1180,7 +1182,7 @@
<string name="ext_media_status_removed" msgid="6576172423185918739">"काढले"</string>
<string name="ext_media_status_unmounted" msgid="2551560878416417752">"बाहेर काढले"</string>
<string name="ext_media_status_checking" msgid="6193921557423194949">"तपासत आहे..."</string>
- <string name="ext_media_status_mounted" msgid="7253821726503179202">"सज्ज"</string>
+ <string name="ext_media_status_mounted" msgid="7253821726503179202">"तयार"</string>
<string name="ext_media_status_mounted_ro" msgid="8020978752406021015">"केवळ-वाचनीय"</string>
<string name="ext_media_status_bad_removal" msgid="8395398567890329422">"असुरक्षितपणे काढले"</string>
<string name="ext_media_status_unmountable" msgid="805594039236667894">"दूषित झाले"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"अनपिन करा"</string>
<string name="app_info" msgid="6856026610594615344">"अॅप माहिती"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"डिव्हाइस रीसेट करायचे?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"डिव्हाइस रीसेट करण्यासाठी टॅप करा"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"डेमो प्रारंभ करत आहे..."</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"डिव्हाइस रीसेट करत आहे..."</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"डिव्हाइस रीसेट करायचे?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"आपण कोणतेही बदल गमवाल आणि डेमो पुन्हा <xliff:g id="TIMEOUT">%1$s</xliff:g> सेकंदांमध्ये प्रारंभ होईल..."</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"रद्द करा"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"आता रीसेट करा"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"हे डिव्हाइस निर्बंधांशिवाय वापरण्यासाठी फॅक्टरी रीसेट करा"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"अधिक जाणून घेण्यासाठी स्पर्श करा."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> अक्षम केले"</string>
+ <string name="conference_call" msgid="3751093130790472426">"परिषद कॉल"</string>
</resources>
diff --git a/core/res/res/values-ms-rMY-watch/styles_material.xml b/core/res/res/values-ms-rMY-watch/styles_material.xml
new file mode 100644
index 0000000..3f5e687
--- /dev/null
+++ b/core/res/res/values-ms-rMY-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"calon"</font></string>
+</resources>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index c4123ab..7db5c74 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Betul!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Cuba lagi"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Cuba lagi"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Buka kunci semua ciri dan data"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Telah melepasi had cubaan Buka Kunci Wajah"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Tiada kad SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Tiada kad SIM dalam tablet."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android sedang menaik taraf..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android sedang dimulakan…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Mengoptimumkan storan."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android sedang ditingkatkan"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Menyelesaikan kemas kini Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Sesetengah apl mungkin tidak berfungsi dengan betul sehingga peningkatan selesai"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> sedang ditingkatkan…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Mengoptimumkan apl <xliff:g id="NUMBER_0">%1$d</xliff:g> daripada <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Menyediakan <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Memulakan apl."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Nyahsemat"</string>
<string name="app_info" msgid="6856026610594615344">"Maklumat apl"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Tetapkan semula peranti?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Ketik untuk menetapkan semula peranti"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Memulakan tunjuk cara…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Menetapkan semula peranti…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Tetapkan semula peranti?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Anda akan kehilangan sebarang perubahan yang dibuat dan tunjuk cara akan dimulakan sekali lagi dalam masa <xliff:g id="TIMEOUT">%1$s</xliff:g> saat…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Batal"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Tetapkan semula sekarang"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Lakukan tetapan semula kilang untuk menggunakan peranti ini tanpa sekatan"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Ketik untuk mengetahui lebih lanjut."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> dilumpuhkan"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Panggilan Sidang"</string>
</resources>
diff --git a/core/res/res/values-my-rMM-watch/styles_material.xml b/core/res/res/values-my-rMM-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-my-rMM-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index 5cd077b..077b889 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"မှန်ပါသည်"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"ထပ် စမ်းပါ"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"ထပ် စမ်းပါ"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"ဝန်ဆောင်မှုနှင့် ဒေတာအားလုံးအတွက် လော့ခ်ဖွင့်ပါ"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"မျက်မှာမှတ် သော့ဖွင့်ခြင်း ခွင့်ပြုသော အကြိမ်ရေထက် ကျော်လွန်သွားပါပြီ"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"ဆင်းကဒ် မရှိပါ"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"တက်ပလက်ထဲတွင်း ဆင်းကဒ် မရှိပါ"</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"အန်ဒရွိုက်ကို မွမ်းမံနေ…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android စတင်နေ…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"သိုလှောင်မှုအား ပြုပြင်ခြင်း။"</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android ကိုအဆင့်မြှင့်တင်နေပါသည်"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android အပ်ဒိတ်ကို အပြီးသတ်နေသည်…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"အဆင့်မြှင့်တင်ခြင်း မပြီးဆုံးသေးသ၍ အချို့အက်ပ်များကို ကောင်းမွန်စွာအသုံးပြုနိုင်ဦးမည် မဟုတ်ပါ"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> ကို အဆင့်မြှင့်တင်နေပါသည်…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_0">%1$d</xliff:g> ထဲက အက်ပ်<xliff:g id="NUMBER_1">%2$d</xliff:g>ကို ဆီလျော်အောင် လုပ်နေ"</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> အားပြင်ဆင်နေသည်။"</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"အက်ပ်များကို စတင်နေ"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"ဖြုတ်ပါ"</string>
<string name="app_info" msgid="6856026610594615344">"အက်ပ်အချက်အလက်"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"စက်ပစ္စည်းကို ပြန်လည်သတ်မှတ်မလား။"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"စက်ပစ္စည်းကို ပြန်လည်သတ်မှတ်ရန် တို့ပါ"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"သရုပ်ပြချက်ကို စတင်နေသည်…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"စက်ပစ္စည်းကို ပြန်လည်သတ်မှတ်နေသည်…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"စက်ပစ္စည်းကို ပြန်လည်သတ်မှတ်မလား။"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"ပြောင်းလဲမှုများကို ဆုံးရှုံးသွားမည်ဖြစ်ပြီး သရုပ်ပြချက်သည် <xliff:g id="TIMEOUT">%1$s</xliff:g> စက္ကန့်အတွင်း ပြန်လည်စတင်ပါမည်…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"မလုပ်တော့"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"ယခုပြန်လည်သတ်မှတ်ပါ"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"ဤစက်ပစ္စည်းကို ကန့်သတ်ချက်များမပါဘဲ အသုံးပြုရန် စက်ရုံထုတ်ဆက်တင်အတိုင်း ပြန်လည်သတ်မှတ်ပါ"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"ပိုမိုလေ့လာရန် တို့ပါ။"</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"ပိတ်ထားသည့် <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"လူအမြောက်အမြားတပြိုင်နက် ခေါ်ဆိုမှု"</string>
</resources>
diff --git a/core/res/res/values-nb-watch/styles_material.xml b/core/res/res/values-nb-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-nb-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 54d496a..c9b275c 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Riktig!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Prøv på nytt"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Prøv på nytt"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Lås opp for å få alle funksjoner og data"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Du har overskredet grensen for opplåsingsforsøk med Ansiktslås"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"SIM-kortet mangler"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Nettbrettet mangler SIM-kort."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android oppgraderes …"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android starter …"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimaliser lagring."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android oppgraderes"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Fullfører Android-oppdatering …"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Noen apper fungerer kanskje ikke skikkelig før oppgraderingen er fullført"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> oppgraderes …"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optimaliserer app <xliff:g id="NUMBER_0">%1$d</xliff:g> av <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Forbereder <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Starter apper."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Løsne"</string>
<string name="app_info" msgid="6856026610594615344">"Info om appen"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Tilbakestille enheten?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Trykk for å tilbakestille enheten"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Starter demo …"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Tilbakestiller enheten …"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Tilbakestille enheten?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Du mister eventuelle endringer, og demoen starter på nytt om <xliff:g id="TIMEOUT">%1$s</xliff:g> sekunder."</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Avbryt"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Tilbakestill nå"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Tilbakestill til fabrikkstandard for å bruke denne enheten uten begrensninger"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Trykk for å finne ut mer."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> er slått av"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Konferansesamtale"</string>
</resources>
diff --git a/core/res/res/values-ne-rNP-watch/styles_material.xml b/core/res/res/values-ne-rNP-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-ne-rNP-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index 138c67f..a0dcfeb 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"सही!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"फेरि प्रयास गर्नुहोस्"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"फेरि प्रयास गर्नुहोस्"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"सबै सुविधाहरू र डेटाका लागि अनलक गर्नुहोस्"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"अत्याधिक मोहडा खोल्ने प्रयासहरू बढी भए।"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"SIM कार्ड छैन"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"ट्याब्लेटमा SIM कार्ड छैन।"</string>
@@ -1028,8 +1029,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"एन्ड्रोइड अपग्रेड हुँदैछ…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android शुरू हुँदैछ..."</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"भण्डारण अनुकूलन गर्दै।"</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android को स्तरवृद्धि हुँदैछ"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android को अद्यावधिकलाई सम्पन्न गर्दै…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"स्तरवृद्धि सम्पन्न नभएसम्म केही अनुप्रयोगहरू राम्ररी काम नगर्न सक्छन्"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> को स्तरवृद्धि हुँदैछ…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"अनुप्रयोग अनुकुल हुँदै <xliff:g id="NUMBER_0">%1$d</xliff:g> को <xliff:g id="NUMBER_1">%2$d</xliff:g>।"</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> तयारी गर्दै।"</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"सुरुवात अनुप्रयोगहरू।"</string>
@@ -1605,7 +1607,7 @@
<string name="zen_mode_feature_name" msgid="5254089399895895004">"अवरोध नपुर्याउँनुहोस्"</string>
<string name="zen_mode_downtime_feature_name" msgid="2626974636779860146">"डाउनटाइम"</string>
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"हरेक हप्तादिनको राति"</string>
- <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"शनिवार"</string>
+ <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"शनिबार"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"घटना"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> द्वारा मौन गरिएको"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"तपाईंको यन्त्रसँग आन्तरिक समस्या छ, र तपाईंले फ्याक्ट्री डाटा रिसेट नगर्दासम्म यो अस्थिर रहन्छ।"</string>
@@ -1657,7 +1659,16 @@
<string name="unpin_target" msgid="3556545602439143442">"अनपिन गर्नुहोस्"</string>
<string name="app_info" msgid="6856026610594615344">"अनुप्रयोगका बारे जानकारी"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"यन्त्रलाई रिसेट गर्ने हो?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"यन्त्रलाई रिसेट गर्न ट्याप गर्नुहोस्"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"डेमो सुरु गर्दै…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"यन्त्रलाई रिसेट गर्दै…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"यन्त्रलाई रिसेट गर्ने हो?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"तपाईँ सबै परिवर्तनहरू गुमाउनु हुनेछ र <xliff:g id="TIMEOUT">%1$s</xliff:g> सेकेन्डमा डेमो फेरि सुरु हुनेछ…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"रद्द गर्नुहोस्"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"अहिले रिसेट गर्नुहोस्"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"यस यन्त्रलाई सीमितताहरू बिना प्रयोग गर्नका लागि फ्याक्ट्री रिसेट गर्नुहोस्"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"थप जान्नका लागि छुनुहोस्।"</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> लाई असक्षम गरियो"</string>
+ <string name="conference_call" msgid="3751093130790472426">"सम्मेलन कल"</string>
</resources>
diff --git a/core/res/res/values-nl-watch/styles_material.xml b/core/res/res/values-nl-watch/styles_material.xml
new file mode 100644
index 0000000..b821347
--- /dev/null
+++ b/core/res/res/values-nl-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"kandidaten"</font></string>
+</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index c8a804b..d7f10d3 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Juist!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Opnieuw proberen"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Nogmaals proberen"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Ontgrendelen voor alle functies en gegevens"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maximaal aantal pogingen voor Ontgrendelen via gezichtsherkenning overschreden"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Geen simkaart"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Geen SIM-kaart in tablet."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android wordt bijgewerkt..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android wordt gestart…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Opslagruimte wordt geoptimaliseerd."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android wordt geüpgraded"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android-update voltooien…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Sommige apps werken mogelijk pas correct nadat de upgrade is voltooid"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> upgraden…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> van <xliff:g id="NUMBER_1">%2$d</xliff:g> optimaliseren."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> voorbereiden."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Apps starten."</string>
@@ -1213,7 +1215,7 @@
<string name="deny" msgid="2081879885755434506">"Weigeren"</string>
<string name="permission_request_notification_title" msgid="6486759795926237907">"Toestemming gevraagd"</string>
<string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"Toestemming gevraagd\nvoor account <xliff:g id="ACCOUNT">%s</xliff:g>."</string>
- <string name="forward_intent_to_owner" msgid="1207197447013960896">"U gebruikt deze app buiten je werkprofiel"</string>
+ <string name="forward_intent_to_owner" msgid="1207197447013960896">"Je gebruikt deze app buiten je werkprofiel"</string>
<string name="forward_intent_to_work" msgid="621480743856004612">"U gebruikt deze app in je werkprofiel"</string>
<string name="input_method_binding_label" msgid="1283557179944992649">"Invoermethode"</string>
<string name="sync_binding_label" msgid="3687969138375092423">"Synchroniseren"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Losmaken"</string>
<string name="app_info" msgid="6856026610594615344">"App-info"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Apparaat resetten?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Tik om apparaat te resetten"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Demo starten…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Apparaat resetten…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Apparaat resetten?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Je wijzigingen gaan verloren. De demo wordt opnieuw gestart over <xliff:g id="TIMEOUT">%1$s</xliff:g> seconden…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Annuleren"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Nu resetten"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Zet dit apparaat terug op de fabrieksinstellingen om het zonder beperkingen te gebruiken"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Tik voor meer informatie."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> uitgeschakeld"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Telefonische vergadering"</string>
</resources>
diff --git a/core/res/res/values-watch/dimens.xml b/core/res/res/values-notround-watch/dimens.xml
similarity index 100%
rename from core/res/res/values-watch/dimens.xml
rename to core/res/res/values-notround-watch/dimens.xml
diff --git a/core/res/res/values-notround-watch/dimens_material.xml b/core/res/res/values-notround-watch/dimens_material.xml
new file mode 100644
index 0000000..9cacb11
--- /dev/null
+++ b/core/res/res/values-notround-watch/dimens_material.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+ <dimen name="dialog_padding_material">8dp</dimen>
+ <dimen name="preference_fragment_padding_vertical_material">0dp</dimen>
+
+ <dimen name="list_item_padding_horizontal_material">16dp</dimen>
+ <dimen name="list_item_padding_start_material">16dp</dimen>
+ <dimen name="list_item_padding_end_material">16dp</dimen>
+
+ <dimen name="dialog_list_padding_top_no_title">8dp</dimen>
+ <dimen name="dialog_list_padding_bottom_no_buttons">8dp</dimen>
+</resources>
diff --git a/packages/DocumentsUI/res/values/attrs.xml b/core/res/res/values-notround-watch/styles_material.xml
similarity index 72%
copy from packages/DocumentsUI/res/values/attrs.xml
copy to core/res/res/values-notround-watch/styles_material.xml
index 9e13001..cd8521f4 100644
--- a/packages/DocumentsUI/res/values/attrs.xml
+++ b/core/res/res/values-notround-watch/styles_material.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
@@ -14,7 +14,5 @@
limitations under the License.
-->
<resources>
- <declare-styleable name="DocumentsTheme">
- <attr name="colorActionMode" format="color"/>
- </declare-styleable>
+ <style name="TextAppearance.Material.AlertDialogMessage" parent="TextAppearance.Material.Body1"/>
</resources>
diff --git a/core/res/res/values-pa-rIN-watch/styles_material.xml b/core/res/res/values-pa-rIN-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-pa-rIN-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-pa-rIN/strings.xml b/core/res/res/values-pa-rIN/strings.xml
index a4e5b89..e717e69 100644
--- a/core/res/res/values-pa-rIN/strings.xml
+++ b/core/res/res/values-pa-rIN/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"ਸਹੀ!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"ਸਾਰੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਅਤੇ ਡੈਟੇ ਲਈ ਅਨਲੌਕ ਕਰੋ"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"ਅਧਿਕਤਮ ਚਿਹਰਾ ਅਨਲੌਕ ਕੋਸ਼ਿਸ਼ਾਂ ਵਧੀਆਂ"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"ਕੋਈ SIM ਕਾਰਡ ਨਹੀਂ"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"ਟੈਬਲੇਟ ਵਿੱਚ ਕੋਈ SIM ਕਾਰਡ ਨਹੀਂ।"</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android ਅਪਗ੍ਰੇਡ ਕਰ ਰਿਹਾ ਹੈ…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android ਚਾਲੂ ਕਰ ਰਿਹਾ ਹੈ…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"ਸਟੋਰੇਜ ਅਨੁਕੂਲ ਕਰ ਰਿਹਾ ਹੈ।"</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android ਅੱਪਗ੍ਰੇਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android ਅੱਪਡੇਟ ਮੁਕੰਮਲ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਕੁਝ ਐਪਾਂ ਅੱਪਗ੍ਰੇਡ ਦੇ ਪੂਰੀ ਹੋਣ ਤੱਕ ਸਹੀ ਢੰਗ ਨਾਲ ਕੰਮ ਨਾ ਕਰਨ"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> ਅੱਪਗ੍ਰੇਡ ਹੋ ਰਹੀ ਹੈ…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_0">%1$d</xliff:g> <xliff:g id="NUMBER_1">%2$d</xliff:g> ਦਾ ਐਪ ਅਨੁਕੂਲ ਕਰ ਰਿਹਾ ਹੈ।"</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> ਤਿਆਰ ਕਰ ਰਿਹਾ ਹੈ।"</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"ਐਪਸ ਚਾਲੂ ਕਰ ਰਿਹਾ ਹੈ।"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"ਅਨਪਿੰਨ ਕਰੋ"</string>
<string name="app_info" msgid="6856026610594615344">"ਐਪ ਜਾਣਕਾਰੀ"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"ਕੀ ਡੀਵਾਈਸ ਮੁੜ-ਸੈੱਟ ਕਰਨੀ ਹੈ?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"ਡੀਵਾਈਸ ਮੁੜ-ਸੈੱਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"ਡੈਮੋ ਚਾਲੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"ਡੀਵਾਈਸ ਮੁੜ-ਸੈੱਟ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"ਕੀ ਡੀਵਾਈਸ ਮੁੜ-ਸੈੱਟ ਕਰਨੀ ਹੈ?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"ਤੁਸੀਂ ਕਿਸੇ ਵੀ ਤਬਦੀਲੀਆਂ ਨੂੰ ਗੁਆ ਬੈਠੋਂਗੇ ਅਤੇ ਡੈਮੋ <xliff:g id="TIMEOUT">%1$s</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਚਾਲੂ ਕੀਤਾ ਜਾਵੇਗਾ…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"ਰੱਦ ਕਰੋ"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"ਹੁਣੇ ਮੁੜ-ਸੈੱਟ ਕਰੋ"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"ਇਸ ਡੀਵਾਈਸ ਨੂੰ ਬਿਨਾਂ ਪਾਬੰਦੀਆਂ ਦੇ ਵਰਤਣ ਲਈ ਫੈਕਟਰੀ ਰੀਸੈੱਟ ਕਰੋ"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"ਹੋਰ ਜਾਣਨ ਲਈ ਸਪਰਸ਼ ਕਰੋ।"</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"ਕਾਨਫਰੰਸ ਕਾਲ"</string>
</resources>
diff --git a/core/res/res/values-pl-watch/styles_material.xml b/core/res/res/values-pl-watch/styles_material.xml
new file mode 100644
index 0000000..384d91c
--- /dev/null
+++ b/core/res/res/values-pl-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"elementy"</font></string>
+</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index ade4bdc..d9e63a6 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -684,6 +684,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Poprawnie!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Spróbuj ponownie."</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Spróbuj ponownie."</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Odblokowanie wszystkich funkcji i danych"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Przekroczono maksymalną liczbę prób rozpoznania twarzy."</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Brak karty SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Brak karty SIM w tablecie."</string>
@@ -1068,8 +1069,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android jest uaktualniany..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android się uruchamia…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optymalizacja pamięci."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android jest uaktualniany"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Kończę aktualizowanie Androida…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Niektóre aplikacje mogą nie działać prawidłowo, dopóki nie zakończy się aktualizacja."</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"Uaktualniam aplikację <xliff:g id="APPLICATION">%1$s</xliff:g>…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optymalizowanie aplikacji <xliff:g id="NUMBER_0">%1$d</xliff:g> z <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Przygotowuję aplikację <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Uruchamianie aplikacji."</string>
@@ -1723,7 +1725,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Odepnij"</string>
<string name="app_info" msgid="6856026610594615344">"O aplikacji"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Zresetować urządzenie?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Kliknij, by zresetować urządzenie"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Uruchamiam tryb demo…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Resetuję urządzenie…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Zresetować urządzenie?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Stracisz wszystkie wprowadzone zmiany, a tryb demo uruchomi się ponownie za <xliff:g id="TIMEOUT">%1$s</xliff:g> s…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Anuluj"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Resetuj teraz"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Aby używać tego urządzenia bez ograniczeń, przywróć ustawienia fabryczne"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Kliknij, by dowiedzieć się więcej."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Wyłączono: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Połączenie konferencyjne"</string>
</resources>
diff --git a/core/res/res/values-pt-rBR-watch/styles_material.xml b/core/res/res/values-pt-rBR-watch/styles_material.xml
new file mode 100644
index 0000000..898d2fd
--- /dev/null
+++ b/core/res/res/values-pt-rBR-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidatos"</font></string>
+</resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index c05931e..eaaad25c 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correto!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Tente novamente"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Tente novamente"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Desbloqueio para todos os recursos e dados"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"O número máximo de tentativas de Desbloqueio por reconhecimento facial foi excedido"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Sem cartão SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Não há um cartão SIM no tablet."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"O Android está sendo atualizado..."</string>
<string name="android_start_title" msgid="8418054686415318207">"O Android está iniciando..."</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Otimizando o armazenamento."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"O Android está sendo atualizado"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Concluindo atualização do Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Alguns apps podem não funcionar corretamente até que a atualização seja concluída"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> está fazendo upgrade…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Otimizando app <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Preparando <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Iniciando apps."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Liberar guia"</string>
<string name="app_info" msgid="6856026610594615344">"Informações do app"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Redefinir dispositivo?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Toque para redefinir o dispositivo"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Iniciando demonstração…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Redefinindo dispositivo…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Redefinir dispositivo?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Você perderá todas as alterações. A demonstração será iniciada novamente em <xliff:g id="TIMEOUT">%1$s</xliff:g> segundos…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Cancelar"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Reiniciar agora"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Redefinir para a configuração original para usar este dispositivo sem restrições"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Toque para saber mais."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Widget <xliff:g id="LABEL">%1$s</xliff:g> desativado"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Teleconferência"</string>
</resources>
diff --git a/core/res/res/values-pt-rPT-watch/styles_material.xml b/core/res/res/values-pt-rPT-watch/styles_material.xml
new file mode 100644
index 0000000..898d2fd
--- /dev/null
+++ b/core/res/res/values-pt-rPT-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidatos"</font></string>
+</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 398193e..ae115d8 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correcto!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Tentar novamente"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Tentar novamente"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Desbloqueio de todas as funcionalidades e dados"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Excedido o n.º máximo de tentativas de Desbloqueio Através do Rosto"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Nenhum cartão SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Nenhum cartão SIM no tablet."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"O Android está a ser atualizado..."</string>
<string name="android_start_title" msgid="8418054686415318207">"O Android está a iniciar…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"A otimizar o armazenamento."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"O Android está a ser atualizado"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"A terminar a atualização do Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Algumas aplicações podem não funcionar corretamente enquanto a atualização não for concluída"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"O <xliff:g id="APPLICATION">%1$s</xliff:g> está a ser atualizado…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"A otimizar a aplicação <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"A preparar o <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"A iniciar aplicações"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Soltar"</string>
<string name="app_info" msgid="6856026610594615344">"Informações da aplicação"</string>
<string name="negative_duration" msgid="5688706061127375131">"-<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Pretende repor o dispositivo?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Toque para repor o dispositivo"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"A iniciar a demonstração…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"A repor o dispositivo…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Pretende repor o dispositivo?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Perderá todas as alterações e a demonstração começará novamente dentro de <xliff:g id="TIMEOUT">%1$s</xliff:g> segundos…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Cancelar"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Repor agora"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Repor os dados de fábrica para utilizar o dispositivo sem restrições"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Toque para saber mais."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> desativado"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Conferência"</string>
</resources>
diff --git a/core/res/res/values-pt-watch/styles_material.xml b/core/res/res/values-pt-watch/styles_material.xml
new file mode 100644
index 0000000..898d2fd
--- /dev/null
+++ b/core/res/res/values-pt-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidatos"</font></string>
+</resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index c05931e..eaaad25c 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correto!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Tente novamente"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Tente novamente"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Desbloqueio para todos os recursos e dados"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"O número máximo de tentativas de Desbloqueio por reconhecimento facial foi excedido"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Sem cartão SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Não há um cartão SIM no tablet."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"O Android está sendo atualizado..."</string>
<string name="android_start_title" msgid="8418054686415318207">"O Android está iniciando..."</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Otimizando o armazenamento."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"O Android está sendo atualizado"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Concluindo atualização do Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Alguns apps podem não funcionar corretamente até que a atualização seja concluída"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> está fazendo upgrade…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Otimizando app <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Preparando <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Iniciando apps."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Liberar guia"</string>
<string name="app_info" msgid="6856026610594615344">"Informações do app"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Redefinir dispositivo?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Toque para redefinir o dispositivo"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Iniciando demonstração…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Redefinindo dispositivo…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Redefinir dispositivo?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Você perderá todas as alterações. A demonstração será iniciada novamente em <xliff:g id="TIMEOUT">%1$s</xliff:g> segundos…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Cancelar"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Reiniciar agora"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Redefinir para a configuração original para usar este dispositivo sem restrições"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Toque para saber mais."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Widget <xliff:g id="LABEL">%1$s</xliff:g> desativado"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Teleconferência"</string>
</resources>
diff --git a/core/res/res/values-ro-watch/styles_material.xml b/core/res/res/values-ro-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-ro-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index c61b41a..6198f12 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -27,11 +27,11 @@
<string name="terabyteShort" msgid="231613018159186962">"TO"</string>
<string name="petabyteShort" msgid="5637816680144990219">"PO"</string>
<string name="fileSizeSuffix" msgid="8897567456150907538">"<xliff:g id="NUMBER">%1$s</xliff:g> <xliff:g id="UNIT">%2$s</xliff:g>"</string>
- <string name="durationDays" msgid="6652371460511178259">"<xliff:g id="DAYS">%1$d</xliff:g> (de) zile"</string>
+ <string name="durationDays" msgid="6652371460511178259">"<xliff:g id="DAYS">%1$d</xliff:g> zile"</string>
<string name="durationDayHours" msgid="2713107458736744435">"<xliff:g id="DAYS">%1$d</xliff:g> zile <xliff:g id="HOURS">%2$d</xliff:g> h"</string>
<string name="durationDayHour" msgid="7293789639090958917">"<xliff:g id="DAYS">%1$d</xliff:g> zi <xliff:g id="HOURS">%2$d</xliff:g> h"</string>
<string name="durationHours" msgid="4266858287167358988">"<xliff:g id="HOURS">%1$d</xliff:g> h"</string>
- <string name="durationHourMinutes" msgid="9029176248692041549">"<xliff:g id="HOURS">%1$d</xliff:g> h <xliff:g id="MINUTES">%2$d</xliff:g> (de) min"</string>
+ <string name="durationHourMinutes" msgid="9029176248692041549">"<xliff:g id="HOURS">%1$d</xliff:g> h <xliff:g id="MINUTES">%2$d</xliff:g> min"</string>
<string name="durationHourMinute" msgid="2741677355177402539">"<xliff:g id="HOURS">%1$d</xliff:g> h <xliff:g id="MINUTES">%2$d</xliff:g> min"</string>
<string name="durationMinutes" msgid="3134226679883579347">"<xliff:g id="MINUTES">%1$d</xliff:g> min"</string>
<string name="durationMinute" msgid="7155301744174623818">"<xliff:g id="MINUTES">%1$d</xliff:g> min."</string>
@@ -140,7 +140,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"Numai Wi-Fi"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: neredirecționată"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
- <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> după <xliff:g id="TIME_DELAY">{2}</xliff:g> (de) secunde"</string>
+ <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> după <xliff:g id="TIME_DELAY">{2}</xliff:g> secunde"</string>
<string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: neredirecționat"</string>
<string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: neredirecționat"</string>
<string name="fcComplete" msgid="3118848230966886575">"Cod de funcție complet."</string>
@@ -681,6 +681,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Corect!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Încercați din nou"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Încercați din nou"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Deblocați pentru toate funcțiile și datele"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"S-a depășit numărul maxim de încercări pentru Deblocare facială"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Fără SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Nu există card SIM în computerul tablet PC."</string>
@@ -703,19 +704,19 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Consultați Ghidul de utilizare sau contactați Serviciul de relații cu clienții."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"Cardul SIM este blocat."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Se deblochează cardul SIM..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> (de) secunde."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Ați introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> (de) secunde."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Ați introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> (de) secunde."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați tableta cu ajutorul datelor de conectare la Google.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> (de) secunde."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați televizorul cu ajutorul datelor de conectare la Google.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> (de) secunde."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați telefonul cu ajutorul datelor de conectare la Google.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> (de) secunde."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Ați introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Ați introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați tableta cu ajutorul datelor de conectare la Google.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați televizorul cu ajutorul datelor de conectare la Google.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați telefonul cu ajutorul datelor de conectare la Google.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
<string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, aceasta va fi resetată la setările prestabilite din fabrică, iar toate datele de utilizator vor fi pierdute."</string>
<string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a televizorului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, televizorul va reveni la setările prestabilite din fabrică, iar toate datele de utilizator se vor pierde."</string>
<string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acesta va fi resetat la setările prestabilite din fabrică, iar toate datele de utilizator vor fi pierdute."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Tableta va fi acum resetată la setările prestabilite din fabrică."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a televizorului. Televizorul va reveni acum la setările prestabilite din fabrică."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Acesta va fi acum resetat la setările prestabilite din fabrică."</string>
- <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Încercați din nou peste <xliff:g id="NUMBER">%d</xliff:g> (de) secunde."</string>
+ <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Încercați din nou peste <xliff:g id="NUMBER">%d</xliff:g> secunde."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Ați uitat modelul?"</string>
<string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"Deblocare cont"</string>
<string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"Prea multe încercări de desenare a modelului"</string>
@@ -839,7 +840,7 @@
<string name="preposition_for_time" msgid="5506831244263083793">"la <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="preposition_for_year" msgid="5040395640711867177">"în <xliff:g id="YEAR">%s</xliff:g>"</string>
<string name="day" msgid="8144195776058119424">"zi"</string>
- <string name="days" msgid="4774547661021344602">" (de) zile"</string>
+ <string name="days" msgid="4774547661021344602">" zile"</string>
<string name="hour" msgid="2126771916426189481">"oră"</string>
<string name="hours" msgid="894424005266852993">"ore"</string>
<string name="minute" msgid="9148878657703769868">"min"</string>
@@ -1045,8 +1046,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android trece la o versiune superioară..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android pornește..."</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Se optimizează stocarea."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android face upgrade"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Se finalizează actualizarea Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Este posibil ca unele aplicații să nu funcționeze corespunzător până când nu se finalizează upgrade-ul"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"Se face upgrade pentru <xliff:g id="APPLICATION">%1$s</xliff:g>…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Se optimizează aplicația <xliff:g id="NUMBER_0">%1$d</xliff:g> din <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Se pregătește <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Se pornesc aplicațiile."</string>
@@ -1289,7 +1291,7 @@
<string name="gpsVerifYes" msgid="2346566072867213563">"Da"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"Nu"</string>
<string name="sync_too_many_deletes" msgid="5296321850662746890">"Limita pentru ștergere a fost depășită"</string>
- <string name="sync_too_many_deletes_desc" msgid="496551671008694245">"Există <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> (de) elemente șterse pentru <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>, contul <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g>. Ce doriți să faceți?"</string>
+ <string name="sync_too_many_deletes_desc" msgid="496551671008694245">"Există <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> elemente șterse pentru <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>, contul <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g>. Ce doriți să faceți?"</string>
<string name="sync_really_delete" msgid="2572600103122596243">"Ștergeți elementele"</string>
<string name="sync_undo_deletes" msgid="2941317360600338602">"Anulați aceste ștergeri"</string>
<string name="sync_do_nothing" msgid="3743764740430821845">"Nu trebuie să luați nicio măsură deocamdată"</string>
@@ -1407,7 +1409,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Model greșit"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Parolă greșită"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Cod PIN greșit"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Încercați din nou peste <xliff:g id="NUMBER">%1$d</xliff:g> (de) secunde."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Încercați din nou peste <xliff:g id="NUMBER">%1$d</xliff:g> secunde."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Desenați modelul"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Introduceți codul PIN al cardului SIM"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Introduceți codul PIN"</string>
@@ -1429,18 +1431,18 @@
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Nume de utilizator sau parolă nevalide."</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Ați uitat numele de utilizator sau parola?\nAccesați "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Se verifică contul…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Ați introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> (de) secunde."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Ați introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> (de) secunde."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> (de) secunde."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Ați introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Ați introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, aceasta va fi resetată la setările prestabilite din fabrică, iar toate datele de utilizator se vor pierde."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a televizorului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, televizorul va reveni la setările prestabilite din fabrică, iar toate datele de utilizator se vor pierde."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acesta va fi resetat la setările prestabilite din fabrică, iar toate datele de utilizator se vor pierde."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Tableta va fi acum resetată la setările prestabilite din fabrică."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a televizorului. Televizorul va reveni acum la setările prestabilite din fabrică."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Telefonul va fi acum resetat la setările prestabilite din fabrică."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați tableta cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> (de) secunde."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați televizorul cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> (de) secunde."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați telefonul cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> (de) secunde."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați tableta cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați televizorul cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați telefonul cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eliminați"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Ridicați volumul mai sus de nivelul recomandat?\n\nAscultarea la volum ridicat pe perioade lungi de timp vă poate afecta auzul."</string>
@@ -1687,7 +1689,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Anulați fixarea"</string>
<string name="app_info" msgid="6856026610594615344">"Informații despre aplicație"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Resetați dispozitivul?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Atingeți pentru a reseta dispozitivul"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Se pornește demonstrația…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Se resetează dispozitivul…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Resetați dispozitivul?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Veți pierde toate modificările, iar demonstrația va începe din nou peste <xliff:g id="TIMEOUT">%1$s</xliff:g> secunde…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Anulați"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Resetați acum"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Reveniți la setările din fabrică pentru a folosi acest dispozitiv fără restricții"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Atingeți pentru a afla mai multe."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> a fost dezactivat"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Conferință telefonică"</string>
</resources>
diff --git a/packages/SystemUI/res/layout/night_mode_settings.xml b/core/res/res/values-round-watch/config_material.xml
similarity index 64%
copy from packages/SystemUI/res/layout/night_mode_settings.xml
copy to core/res/res/values-round-watch/config_material.xml
index 3725e78..bf445ef 100644
--- a/packages/SystemUI/res/layout/night_mode_settings.xml
+++ b/core/res/res/values-round-watch/config_material.xml
@@ -14,11 +14,9 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <include layout="@layout/switch_bar" />
-
-</LinearLayout>
+<!-- These resources are around just to allow their values to be customized
+ for watch products. Do not translate. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Don't clip on round screens so the list can scroll past the rounded edges. -->
+ <bool name="config_preferenceFragmentClipToPadding">false</bool>
+</resources>
diff --git a/core/res/res/values-round-watch/dimens_material.xml b/core/res/res/values-round-watch/dimens_material.xml
new file mode 100644
index 0000000..f2de4e0
--- /dev/null
+++ b/core/res/res/values-round-watch/dimens_material.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+ <dimen name="dialog_padding_material">@dimen/screen_percentage_15</dimen>
+ <dimen name="preference_fragment_padding_vertical_material">@dimen/screen_percentage_15</dimen>
+
+ <dimen name="list_item_padding_horizontal_material">@dimen/screen_percentage_15</dimen>
+ <dimen name="list_item_padding_start_material">@dimen/screen_percentage_15</dimen>
+ <dimen name="list_item_padding_end_material">@dimen/screen_percentage_10</dimen>
+
+ <dimen name="dialog_list_padding_top_no_title">@dimen/screen_percentage_15</dimen>
+ <dimen name="dialog_list_padding_bottom_no_buttons">@dimen/screen_percentage_15</dimen>
+</resources>
diff --git a/packages/DocumentsUI/res/values/attrs.xml b/core/res/res/values-round-watch/styles_material.xml
similarity index 69%
copy from packages/DocumentsUI/res/values/attrs.xml
copy to core/res/res/values-round-watch/styles_material.xml
index 9e13001..a2f3c02 100644
--- a/packages/DocumentsUI/res/values/attrs.xml
+++ b/core/res/res/values-round-watch/styles_material.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
@@ -14,7 +14,7 @@
limitations under the License.
-->
<resources>
- <declare-styleable name="DocumentsTheme">
- <attr name="colorActionMode" format="color"/>
- </declare-styleable>
+ <style name="TextAppearance.Material.AlertDialogMessage" parent="TextAppearance.Material.Body1">
+ <item name="textAlignment">center</item>
+ </style>
</resources>
diff --git a/core/res/res/values-ru-watch/styles_material.xml b/core/res/res/values-ru-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-ru-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 686e3c2..0ef96af 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -268,7 +268,7 @@
<string name="permgroupdesc_sensors" msgid="7147968539346634043">"доступ к данным датчиков о состоянии организма"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Получать содержимое окна"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Анализировать содержимое активного окна."</string>
- <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Включать аудиоподсказки"</string>
+ <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Включать Изучение касанием"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="7543249041581408313">"Озвучивать нажимаемые элементы и разрешать управление устройством с помощью жестов."</string>
<string name="capability_title_canRequestEnhancedWebAccessibility" msgid="1739881766522594073">"Включать спец. возможности для Интернета"</string>
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Могут быть установлены дополнительные скрипты."</string>
@@ -684,6 +684,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Правильно!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Повторите попытку"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Повторите попытку"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Разблок. для доступа ко всем функциям и данным"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Все попытки войти с помощью Фейсконтроля использованы"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Нет SIM-карты"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"SIM-карта не установлена."</string>
@@ -826,9 +827,9 @@
<string name="searchview_description_clear" msgid="1330281990951833033">"Удалить запрос"</string>
<string name="searchview_description_submit" msgid="2688450133297983542">"Отправить запрос"</string>
<string name="searchview_description_voice" msgid="2453203695674994440">"Голосовой поиск"</string>
- <string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"Включить аудиоподсказки?"</string>
- <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> хочет включить функцию \"Аудиоподсказки\". Она позволяет прослушивать или просматривать описание элементов, которых вы касаетесь, и управлять планшетным ПК с помощью жестов."</string>
- <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> хочет включить функцию \"Аудиоподсказки\". Она позволяет прослушивать или просматривать описание элементов, которых вы касаетесь, и управлять телефоном с помощью жестов."</string>
+ <string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"Включить Изучение касанием?"</string>
+ <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> хочет включить Изучение касанием. Вы сможете прослушивать или просматривать описание элементов, которых касаетесь, и управлять планшетом с помощью жестов."</string>
+ <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> хочет включить Изучение касанием. Вы сможете прослушивать или просматривать описание элементов, которых касаетесь, и управлять телефоном с помощью жестов."</string>
<string name="oneMonthDurationPast" msgid="7396384508953779925">"1 месяц назад"</string>
<string name="beforeOneMonthDurationPast" msgid="909134546836499826">"Более месяца назад"</string>
<plurals name="last_num_days" formatted="false" msgid="5104533550723932025">
@@ -1068,8 +1069,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Обновление Android..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Запуск Android…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Оптимизация хранилища…"</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Обновление Android"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Завершается обновление Android"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Во время обновления возможны неполадки в работе приложений."</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"Обновление приложения \"<xliff:g id="APPLICATION">%1$s</xliff:g>\"…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Оптимизация приложения <xliff:g id="NUMBER_0">%1$d</xliff:g> из <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Подготовка приложения \"<xliff:g id="APPNAME">%1$s</xliff:g>\"..."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Запуск приложений."</string>
@@ -1723,7 +1725,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Открепить"</string>
<string name="app_info" msgid="6856026610594615344">"О приложении"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Сбросить настройки устройства?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Нажмите здесь, чтобы сбросить настройки"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Запуск деморежима…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Сброс настроек…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Сбросить настройки устройства?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Все изменения будут утеряны. Деморежим будет перезапущен через <xliff:g id="TIMEOUT">%1$s</xliff:g> сек."</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Отмена"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Сбросить"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Сброс до заводских настроек для работы без ограничений"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Нажмите, чтобы узнать больше."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Виджет <xliff:g id="LABEL">%1$s</xliff:g> отключен"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Конференц-связь"</string>
</resources>
diff --git a/core/res/res/values-si-rLK-watch/styles_material.xml b/core/res/res/values-si-rLK-watch/styles_material.xml
new file mode 100644
index 0000000..37b4afe
--- /dev/null
+++ b/core/res/res/values-si-rLK-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"අපේක්ෂකයන්"</font></string>
+</resources>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index f5878ce..4f90b57 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"නිවැරදියි!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"නැවත උත්සාහ කරන්න"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"නැවත උත්සාහ කරන්න"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"සියලු විශේෂාංග සහ දත්ත අනවහිර කරන්න"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"මුහුණ භාවිතයෙන් අඟුළු හැරීමේ උපරිම ප්රයන්තයන් ගණන ඉක්මවා ඇත"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"SIM පත නැත"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"ටැබ්ලටයේ SIM පත නොමැත."</string>
@@ -1024,8 +1025,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android උත්ශ්රේණි වෙමින් පවතී..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android ආරම්භ කරමින්…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"ආචයනය ප්රශස්තිකරණය කිරීම."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android උත්ශ්රේණි කරමින්"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android යාවත්කාලීනය අවසන් කරමින්…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"උත්ශ්රේණි කිරීම අවසන් වන තෙක් සමහර යෙදුම් නිසි ලෙස ක්රියා නොකළ හැකිය"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> උත්ශ්රේණි කරමින්…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> කින් <xliff:g id="NUMBER_0">%1$d</xliff:g> වැනි යෙදුම ප්රශස්ත කරමින්."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> සූදානම් කරමින්."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"යෙදුම් ආරම්භ කරමින්."</string>
@@ -1653,7 +1655,16 @@
<string name="unpin_target" msgid="3556545602439143442">"ගලවන්න"</string>
<string name="app_info" msgid="6856026610594615344">"යෙදුම් තොරතුරු"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"උපාංගය යළි සකසන්නද?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"උපාංගය යළි සැකසීමට තට්ටු කරන්න"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"ආදර්ශනය ආරම්භ කරමින්..."</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"උපාංගය යළි සකසමින්..."</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"උපාංගය යළි සකසන්නද?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"ඔබට යම් වෙනස් කිරීම් අහිමි වනු ඇති අතර ආදර්ශනය තත්පර <xliff:g id="TIMEOUT">%1$s</xliff:g>කින් නැවත ආරම්භ වනු ඇත…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"අවලංගු කරන්න"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"දැන් යළි සකසන්න"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"සීමා කිරීම්වලින් තොරව මෙම උපාංගය භාවිත කිරීමට කර්මාන්ත ශාලා යළි සැකසීම"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"තව දැන ගැනීමට ස්පර්ශ කරන්න."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"අබල කළ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"සම්මන්ත්රණ ඇමතුම"</string>
</resources>
diff --git a/core/res/res/values-sk-watch/styles_material.xml b/core/res/res/values-sk-watch/styles_material.xml
new file mode 100644
index 0000000..5b604e8
--- /dev/null
+++ b/core/res/res/values-sk-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"kandidáti"</font></string>
+</resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 2cb7ecd..0a48693 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -249,23 +249,23 @@
<string name="user_owner_label" msgid="1119010402169916617">"Prepnúť na osobný"</string>
<string name="managed_profile_label" msgid="5289992269827577857">"Prepnúť na pracovný"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakty"</string>
- <string name="permgroupdesc_contacts" msgid="6951499528303668046">"prístup k vašim kontaktom"</string>
+ <string name="permgroupdesc_contacts" msgid="6951499528303668046">"prístup ku kontaktom"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Poloha"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"prístup k polohe tohto zariadenia"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendár"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"prístup ku kalendáru"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
- <string name="permgroupdesc_sms" msgid="4656988620100940350">"posielať a zobrazovať SMS"</string>
+ <string name="permgroupdesc_sms" msgid="4656988620100940350">"posielanie a zobrazovanie SMS"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Úložisko"</string>
- <string name="permgroupdesc_storage" msgid="637758554581589203">"prístup k fotkám, médiám a súborom na zariadení"</string>
+ <string name="permgroupdesc_storage" msgid="637758554581589203">"prístup k fotkám, médiám a súborom v zariadení"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofón"</string>
- <string name="permgroupdesc_microphone" msgid="4988812113943554584">"zaznamenávanie zvuku"</string>
+ <string name="permgroupdesc_microphone" msgid="4988812113943554584">"nahrávanie zvuku"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Fotoaparát"</string>
<string name="permgroupdesc_camera" msgid="3250611594678347720">"fotenie a natáčanie videí"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Telefón"</string>
- <string name="permgroupdesc_phone" msgid="6234224354060641055">"telefonovať a spravovať hovory"</string>
+ <string name="permgroupdesc_phone" msgid="6234224354060641055">"telefonovanie a správu hovorov"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Telesné senzory"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"prístup k údajom senzorov o životných funkciách"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"prístup k dátam senzorov vašich životných funkcií"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Načítať obsah okna"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Môžete preskúmať obsah okna, s ktorým pracujete."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Zapnúť funkciu Preskúmanie dotykom"</string>
@@ -684,6 +684,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Správne!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Skúsiť znova"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Skúsiť znova"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Všetky funkcie a dáta získate po odomknutí"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Prekročili ste maximálny povolený počet pokusov o odomknutie tvárou"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Nie je vložená SIM karta"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"V tablete nie je žiadna SIM karta."</string>
@@ -1068,8 +1069,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Prebieha inovácia systému Android..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Systém Android sa spúšťa…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimalizuje sa úložisko"</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Prebieha inovácia systému Android"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Dokončuje sa aktualizácia Androidu…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Niektoré aplikácie môžu správne fungovať až po dokončení inovácie"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"Aplikácia <xliff:g id="APPLICATION">%1$s</xliff:g> sa inovuje…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Prebieha optimalizácia aplikácie <xliff:g id="NUMBER_0">%1$d</xliff:g> z <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Pripravuje sa aplikácia <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Prebieha spúšťanie aplikácií."</string>
@@ -1723,7 +1725,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Uvoľniť"</string>
<string name="app_info" msgid="6856026610594615344">"Info o aplikácii"</string>
<string name="negative_duration" msgid="5688706061127375131">"-<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Resetovať zariadenie?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Klepnutím resetujete zariadenie"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Spúšťa sa ukážka…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Resetuje sa zariadenie…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Resetovať zariadenie?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Prídete o všetky zmeny a ukážka sa znova spustí o <xliff:g id="TIMEOUT">%1$s</xliff:g> s…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Zrušiť"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Resetovať"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Ak chcete toto zariadenie používať bez obmedzení, obnovte na ňom továrenské nastavenia"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Klepnutím získate ďalšie informácie."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Deaktivovaná miniaplikácia <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Konferenčný hovor"</string>
</resources>
diff --git a/core/res/res/values-sl-watch/styles_material.xml b/core/res/res/values-sl-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-sl-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 9ece9eb..7b8a03a 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -684,6 +684,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Pravilno."</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Poskusi znova"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Poskusite znova"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Odklenite za dostop do vseh funkcij in podatkov"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Presegli ste dovoljeno število poskusov odklepanja z obrazom"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Ni kartice SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"V tabličnem računalniku ni kartice SIM."</string>
@@ -1068,8 +1069,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Poteka nadgradnja Androida ..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android se zaganja …"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimiziranje shrambe."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Poteka nadgradnja Androida."</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Dokončanje posodobitve Androida …"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Nekatere aplikacije morda ne bodo delovale pravilno, dokler ne bo dokončana nadgradnja."</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> se nadgrajuje …"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optimiranje aplikacije <xliff:g id="NUMBER_0">%1$d</xliff:g> od <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Pripravljanje aplikacije <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Zagon aplikacij."</string>
@@ -1723,7 +1725,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Odpenjanje"</string>
<string name="app_info" msgid="6856026610594615344">"Podatki o aplikaciji"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Želite ponastaviti napravo?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Dotaknite se, če želite ponastaviti napravo"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Začenjanje predstavitve …"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Ponastavljanje naprave …"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Želite ponastaviti napravo?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Morebitne spremembe bodo izgubljene in predstavitev se bo začela znova čez <xliff:g id="TIMEOUT">%1$s</xliff:g> s …"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Prekliči"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Ponastavi"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Ponastavitev naprave na tovarniške nastavitve za uporabo brez omejitev"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Dotaknite se, če želite izvedeti več."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> – onemogočeno"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Konferenčni klic"</string>
</resources>
diff --git a/core/res/res/values-sq-rAL-watch/styles_material.xml b/core/res/res/values-sq-rAL-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-sq-rAL-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index 9dcd8e6..05f0e59 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -245,7 +245,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktet"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"qasu te kontaktet e tua"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Vendndodhja"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"qasjen te vendndodhja e kësaj pajisjeje"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"qaset te vendndodhja e kësaj pajisjeje"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendari"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"qasje te kalendari yt"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Saktë!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Provo sërish"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Provo sërish"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Shkyçe për të gjitha funksionet dhe të dhënat"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Tentativat maksimale të \"Shkyçjes me fytyrë\" u tejkaluan"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Nuk ka kartë SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Nuk ka kartë SIM në tablet."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"\"Androidi\" po përditësohet…"</string>
<string name="android_start_title" msgid="8418054686415318207">"\"Androidi\" po fillon…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Po përshtat ruajtjen."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android po përmirësohet"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Përditësimi i Android po përfundon…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Disa aplikacione mund të mos funksionojnë si duhet deri sa të përfundojë përmirësimi"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> po përmirësohet…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Po përshtat aplikacionin <xliff:g id="NUMBER_0">%1$d</xliff:g> nga gjithsej <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Po përgatit <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Aplikacionet e fillimit."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Zhgozhdo"</string>
<string name="app_info" msgid="6856026610594615344">"Informacioni mbi aplikacionin"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Do ta rivendosësh pajisjen?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Trokit për ta rivendosur pajisjen"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Po nis demonstrimin..."</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Po rivendos pajisjen…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Do ta rivendosësh pajisjen?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Do të humbasësh çdo ndryshim dhe demonstrimi do të niset përsëri për <xliff:g id="TIMEOUT">%1$s</xliff:g> sekonda…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Anulo"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Rivendos tani"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Rivendos cilësimet e fabrikës për ta përdorur këtë pajisje pa kufizime"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Prek për të mësuar më shumë."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> u çaktivizua"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Telefonatë konferencë"</string>
</resources>
diff --git a/core/res/res/values-sr-watch/styles_material.xml b/core/res/res/values-sr-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-sr-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 4eb13f5..517d777 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -681,6 +681,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Тачно!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Покушајте поново"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Покушајте поново"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Откључај за све функције и податке"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Премашен је највећи дозвољени број покушаја Откључавања лицем"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Нема SIM картице"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"У таблету нема SIM картице."</string>
@@ -1045,8 +1046,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android се надограђује…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android се покреће…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Меморија се оптимизује."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android се надограђује…"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Довршавамо ажурирање Android-а…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Неке апликације можда неће исправно функционисати док се надоградња не доврши"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> се надограђује…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Оптимизовање апликације <xliff:g id="NUMBER_0">%1$d</xliff:g> од <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Припрема се <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Покретање апликација."</string>
@@ -1687,7 +1689,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Откачи"</string>
<string name="app_info" msgid="6856026610594615344">"Информације о апликацији"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Желите ли да ресетујете уређај?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Додирните да бисте ресетовали уређај"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Покрећемо демонстрацију..."</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Ресетујемо уређај..."</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Желите ли да ресетујете уређај?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Изгубићете све промене и демонстрација ће поново почети за <xliff:g id="TIMEOUT">%1$s</xliff:g> сек…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Откажи"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Ресетуј"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Ресетујте уређај на фабричка подешавања да бисте га користили без ограничења"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Додирните да бисте сазнали више."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Виџет <xliff:g id="LABEL">%1$s</xliff:g> је онемогућен"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Конференцијски позив"</string>
</resources>
diff --git a/core/res/res/values-sv-watch/styles_material.xml b/core/res/res/values-sv-watch/styles_material.xml
new file mode 100644
index 0000000..f2ab18a
--- /dev/null
+++ b/core/res/res/values-sv-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"kandidater"</font></string>
+</resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 21016d6..cf1b19c 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Korrekt!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Försök igen"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Försök igen"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Lås upp för alla funktioner och all data"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Du har försökt låsa upp med Ansiktslås för många gånger"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Inget SIM-kort"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Inget SIM-kort i surfplattan."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android uppgraderas ..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android startar …"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Lagringsutrymmet optimeras."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android uppgraderas"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android-uppdateringen slutförs …"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"En del appar kanske inte fungerar som de ska innan uppgraderingen har slutförts"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> uppgraderas …"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optimerar app <xliff:g id="NUMBER_0">%1$d</xliff:g> av <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> förbereds."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Appar startas."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Lossa"</string>
<string name="app_info" msgid="6856026610594615344">"Info om appen"</string>
<string name="negative_duration" msgid="5688706061127375131">"-<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Vill du återställa enheten?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Tryck om du vill återställa enheten"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Demo startas …"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Enheten återställs …"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Vill du återställa enheten?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Ändringar som du har gjort sparas inte och demon börjar om om <xliff:g id="TIMEOUT">%1$s</xliff:g> sekunder …"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Avbryt"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Återställ nu"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Återställ enheten till standardinställningarna om du vill använda den utan begränsningar"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Tryck här om du vill läsa mer."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> har inaktiverats"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Konferenssamtal"</string>
</resources>
diff --git a/core/res/res/values-sw-watch/styles_material.xml b/core/res/res/values-sw-watch/styles_material.xml
new file mode 100644
index 0000000..01392b2
--- /dev/null
+++ b/core/res/res/values-sw-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"yanayopendekezwa"</font></string>
+</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 8d9b151..3daaf93 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -676,6 +676,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Sahihi!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Jaribu tena"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Jaribu tena"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Fungua kifaa ili upate data na vipengele vyote"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Majaribio ya Juu ya Kufungua Uso yamezidishwa"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Hakuna SIM kadi"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Hakuna SIM kadi katika kompyuta ndogo."</string>
@@ -1020,8 +1021,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Toleo jipya la Android linawekwa..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Inaanzisha Android..."</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Inaboresha hifadhi."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Tunasasisha Android"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Inakamilisha kusasisha Android..."</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Huenda baadhi ya programu zisifanye kazi vizuri hadi itakapomaliza kusasisha"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> inapata toleo jipya…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Inaboresha programu <xliff:g id="NUMBER_0">%1$d</xliff:g> kutoka <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Inaandaa <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Programu zinaanza"</string>
@@ -1649,7 +1651,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Bandua"</string>
<string name="app_info" msgid="6856026610594615344">"Maelezo ya programu"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Ungependa kuweka upya mipangilio ya kifaa?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Gonga ili uweke upya kifaa"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Inaanzisha onyesho..."</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Inaweka upya kifaa..."</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Ungependa kuweka upya mipangilio ya kifaa?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Mabadiliko yoyote hayatahifadhiwa. Onyesho litaanza tena baada ya sekunde <xliff:g id="TIMEOUT">%1$s</xliff:g>..."</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Ghairi"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Weka upya sasa"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Rejesha mipangilio iliyotoka nayo kiwandani ili utumie kifaa hiki bila vikwazo"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Gusa ili kupata maelezo zaidi."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> imezimwa"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Simu ya Kongamano"</string>
</resources>
diff --git a/core/res/res/values-ta-rIN-watch/styles_material.xml b/core/res/res/values-ta-rIN-watch/styles_material.xml
new file mode 100644
index 0000000..d4605e6
--- /dev/null
+++ b/core/res/res/values-ta-rIN-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"கேன்டிடேட்ஸ்"</font></string>
+</resources>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index c4c5bc3..e851399 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -243,23 +243,23 @@
<string name="user_owner_label" msgid="1119010402169916617">"தனிப்பட்ட சுயவிவரத்திற்கு மாறு"</string>
<string name="managed_profile_label" msgid="5289992269827577857">"பணிச் சுயவிவரத்திற்கு மாறு"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"தொடர்புகள்"</string>
- <string name="permgroupdesc_contacts" msgid="6951499528303668046">"தொடர்புகளை அணுகும்"</string>
+ <string name="permgroupdesc_contacts" msgid="6951499528303668046">"தொடர்புகளை அணுக வேண்டும்"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"இருப்பிடம்"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"சாதனத்தின் இருப்பிடத்தை அணுகும்"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"இந்தச் சாதனத்தின் இருப்பிடத்தை அறிந்து கொள்ளலாம்"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"கேலெண்டர்"</string>
- <string name="permgroupdesc_calendar" msgid="3889615280211184106">"கேலெண்டரை அணுகும்"</string>
+ <string name="permgroupdesc_calendar" msgid="3889615280211184106">"கேலெண்டரை அணுகலாம்"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
- <string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS செய்திகளை அனுப்பும் மற்றும் பார்க்கும்"</string>
+ <string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS அனுப்பலாம், வந்த SMSகளைப் பார்க்கலாம்"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"சேமிப்பிடம்"</string>
- <string name="permgroupdesc_storage" msgid="637758554581589203">"உங்கள் சாதனத்தில் உள்ள படங்கள், மீடியா மற்றும் கோப்புகளை அணுகும்"</string>
+ <string name="permgroupdesc_storage" msgid="637758554581589203">"உங்கள் சாதனத்தில் உள்ள படங்கள், மீடியா மற்றும் கோப்புகளை அணுக வேண்டும்"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"மைக்ரோஃபோன்"</string>
- <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ஆடியோவைப் பதிவுசெய்யும்"</string>
+ <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ஒலிப் பதிவு செய்யலாம்"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"கேமரா"</string>
- <string name="permgroupdesc_camera" msgid="3250611594678347720">"படங்களை எடுக்கும், வீடியோவைப் பதிவுசெய்யும்"</string>
+ <string name="permgroupdesc_camera" msgid="3250611594678347720">"படங்கள் மற்றும் வீடியோக்கள் எடுக்கலாம்"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"ஃபோன்"</string>
- <string name="permgroupdesc_phone" msgid="6234224354060641055">"மொபைல் அழைப்புகளைச் செய்யும், பெறும்"</string>
+ <string name="permgroupdesc_phone" msgid="6234224354060641055">"யாரையும் தொலைபேசியில் அழைக்கலாம்"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"உடல் உணர்விகள்"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"உங்கள் உடலியக்கக் குறிகள் பற்றிய உணர்வித் தரவை அணுகும்"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"உங்கள் உடல் இயக்கம் பற்றி உணர்விகள் கூறும் தகவலைப் பார்க்கலாம்"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"சாளர உள்ளடக்கத்தைப் பெறும்"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"நீங்கள் பணியாற்றி கொண்டிருக்கும் சாளரத்தின் உள்ளடக்கத்தைப் பார்க்கலாம்."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"தொடுவதன் மூலம் அறிவதை இயக்கும்"</string>
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"சரி!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"மீண்டும் முயற்சிக்கவும்"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"மீண்டும் முயற்சிக்கவும்"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"எல்லா அம்சங்கள் & தரவை பெற, சாதனத்தை திறக்கவும்"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"முகம் திறப்பதற்கான அதிகபட்ச முயற்சிகள் கடந்தன"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"சிம் கார்டு இல்லை"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"டேப்லெட்டில் சிம் கார்டு இல்லை."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android மேம்படுத்தப்படுகிறது…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android துவங்குகிறது..."</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"சேமிப்பகத்தை உகந்ததாக்குகிறது."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android மேம்படுத்தப்படுகிறது"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android புதுப்பிப்பை நிறைவுசெய்கிறது…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"மேம்படுத்துவது முடியும் வரை, சில பயன்பாடுகள் சரியாக வேலைசெய்யாமல் போகக்கூடும்"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g>ஐ மேம்படுத்துகிறது…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_0">%1$d</xliff:g> / <xliff:g id="NUMBER_1">%2$d</xliff:g> பயன்பாட்டை ஒருங்கிணைக்கிறது."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g>ஐத் தயார்செய்கிறது."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"பயன்பாடுகள் தொடங்கப்படுகின்றன."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"பின்னை அகற்று"</string>
<string name="app_info" msgid="6856026610594615344">"பயன்பாட்டுத் தகவல்"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"சாதனத்தை மீட்டமைக்கவா?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"சாதனத்தை மீட்டமைக்க, தட்டவும்"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"டெமோவைத் தொடங்குகிறது…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"சாதனத்தை மீட்டமைக்கிறது…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"சாதனத்தை மீட்டமைக்கவா?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"மாற்றங்கள் சேமிக்கப்படாது, <xliff:g id="TIMEOUT">%1$s</xliff:g> வினாடிகளில் டெமோ மீண்டும் தொடங்கும்…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"ரத்துசெய்"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"இப்போதே மீட்டமை"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"இந்தச் சாதனத்தைக் கட்டுப்பாடுகளின்றிப் பயன்படுத்த, ஆரம்ப நிலைக்கு மீட்டமைக்கவும்"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"மேலும் அறிய தொடவும்."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"முடக்கப்பட்டது: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"குழு அழைப்பு"</string>
</resources>
diff --git a/core/res/res/values-te-rIN-watch/styles_material.xml b/core/res/res/values-te-rIN-watch/styles_material.xml
new file mode 100644
index 0000000..877cd58
--- /dev/null
+++ b/core/res/res/values-te-rIN-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"క్యాండిడేట్లు"</font></string>
+</resources>
diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml
index 1cfab6e..7c48c8c 100644
--- a/core/res/res/values-te-rIN/strings.xml
+++ b/core/res/res/values-te-rIN/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"సరైనది!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"మళ్లీ ప్రయత్నించండి"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"మళ్లీ ప్రయత్నించండి"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"అన్ని లక్షణాలు మరియు డేటా కోసం అన్లాక్ చేయండి"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"ముఖంతో అన్లాక్ ప్రయత్నాల గరిష్ట పరిమితి మించిపోయారు"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"సిమ్ కార్డు లేదు"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"టాబ్లెట్లో సిమ్ కార్డు లేదు."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android అప్గ్రేడ్ అవుతోంది…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android ప్రారంభమవుతోంది…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"నిల్వను అనుకూలపరుస్తోంది."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android అప్గ్రేడ్ అవుతోంది"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android నవీకరణను ముగిస్తోంది…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"అప్గ్రేడ్ పూర్తయ్యే వరకు కొన్ని అనువర్తనాలు సరిగ్గా పని చేయకపోవచ్చు"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g>ని అప్గ్రేడ్ చేస్తోంది…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g>లో <xliff:g id="NUMBER_0">%1$d</xliff:g> అనువర్తనాన్ని అనుకూలీకరిస్తోంది."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g>ని సిద్ధం చేస్తోంది."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"అనువర్తనాలను ప్రారంభిస్తోంది."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"అన్పిన్ చేయి"</string>
<string name="app_info" msgid="6856026610594615344">"అనువర్తన సమాచారం"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"పరికరాన్ని రీసెట్ చేయాలా?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"పరికరాన్ని రీసెట్ చేయడానికి నొక్కండి"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"డెమోను ప్రారంభిస్తోంది..."</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"పరికరాన్ని రీసెట్ చేస్తోంది..."</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"పరికరాన్ని రీసెట్ చేయాలా?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"మీరు చేసిన ఏవైనా మార్పులను కోల్పోతారు మరియు డెమో <xliff:g id="TIMEOUT">%1$s</xliff:g> సెకన్లలో మళ్లీ ప్రారంభమవుతుంది…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"రద్దు చేయి"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"ఇప్పుడే రీసెట్ చేయి"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"ఈ పరికరాన్ని ఎటువంటి పరిమితులు లేకుండా ఉపయోగించడానికి ఫ్యాక్టరీ రీసెట్ చేయండి"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"మరింత తెలుసుకోవడానికి తాకండి."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> నిలిపివేయబడింది"</string>
+ <string name="conference_call" msgid="3751093130790472426">"కాన్ఫరెన్స్ కాల్"</string>
</resources>
diff --git a/core/res/res/values-th-watch/styles_material.xml b/core/res/res/values-th-watch/styles_material.xml
new file mode 100644
index 0000000..3227ced
--- /dev/null
+++ b/core/res/res/values-th-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"ผู้สมัคร"</font></string>
+</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 581bb81..90d9f9b 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"ถูกต้อง!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"ลองอีกครั้ง"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"ลองอีกครั้ง"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"ปลดล็อกคุณลักษณะและข้อมูลทั้งหมด"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"มีความพยายามที่จะใช้ Face Unlock เกินขีดจำกัด"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"ไม่มีซิมการ์ด"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"ไม่มีซิมการ์ดในแท็บเล็ต"</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"กำลังอัปเกรด Android ..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android กำลังเริ่มต้น…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"กำลังเพิ่มประสิทธิภาพพื้นที่จัดเก็บข้อมูล"</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android กำลังอัปเกรด"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"กำลังทำการอัปเดต Android ให้เสร็จสิ้น…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"แอปบางแอปอาจทำงานไม่ถูกต้องจนกว่าจะอัปเกรดเสร็จ"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"กำลังอัปเกรด <xliff:g id="APPLICATION">%1$s</xliff:g>…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"กำลังเพิ่มประสิทธิภาพแอปพลิเคชัน <xliff:g id="NUMBER_0">%1$d</xliff:g> จาก <xliff:g id="NUMBER_1">%2$d</xliff:g> รายการ"</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"กำลังเตรียม <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"กำลังเริ่มต้นแอปพลิเคชัน"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"เลิกปักหมุด"</string>
<string name="app_info" msgid="6856026610594615344">"ข้อมูลแอป"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"รีเซ็ตอุปกรณ์ไหม"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"แตะเพื่อรีเซ็ตอุปกรณ์"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"กำลังเริ่มการสาธิต…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"กำลังรีเซ็ตอุปกรณ์…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"รีเซ็ตอุปกรณ์ไหม"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"การเปลี่ยนแปลงของคุณจะหายไปและการสาธิตจะเริ่มต้นอีกครั้งใน <xliff:g id="TIMEOUT">%1$s</xliff:g> วินาที…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"ยกเลิก"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"รีเซ็ตทันที"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"รีเซ็ตเป็นค่าเริ่มต้นเพื่อใช้อุปกรณ์นี้โดยไร้ข้อจำกัด"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"แตะเพื่อเรียนรู้เพิ่มเติม"</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"ปิดใช้ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"การประชุมสาย"</string>
</resources>
diff --git a/core/res/res/values-tl-watch/styles_material.xml b/core/res/res/values-tl-watch/styles_material.xml
new file mode 100644
index 0000000..70e7a7a
--- /dev/null
+++ b/core/res/res/values-tl-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"mga kandidato"</font></string>
+</resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index df4dd9b..efb319f 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Tama!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Subukang muli"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Subukang muli"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"I-unlock para sa lahat ng feature at data"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Nalagpasan na ang maximum na mga pagtatangka sa Face Unlock"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Walang SIM card"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Walang SIM card sa tablet."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Nag-a-upgrade ang Android…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Nagsisimula ang Android…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Ino-optimize ang storage."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Nag-a-upgrade ang Android"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Tinatapos ang pag-update sa Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Maaaring hindi gumana nang maayos ang ilang app hangga\'t hindi pa natatapos ang pag-upgrade"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"Nag-a-upgrade ang <xliff:g id="APPLICATION">%1$s</xliff:g>…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Ino-optimize ang app <xliff:g id="NUMBER_0">%1$d</xliff:g> ng <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Ihinahanda ang <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Sinisimulan ang apps."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"I-unpin"</string>
<string name="app_info" msgid="6856026610594615344">"Impormasyon ng app"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Gusto mo bang i-reset ang device?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Mag-tap upang i-reset ang device"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Sinisimulan ang demo…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Nire-reset ang device…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Gusto mo bang i-reset ang device?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Mawawala mo ang anumang mga pagbabago at magsisimulang muli ang demo pagkalipas ng <xliff:g id="TIMEOUT">%1$s</xliff:g> (na) segundo…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Kanselahin"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"I-reset ngayon"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"I-factory reset upang magamit ang device na ito nang walang mga paghihigpit"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Pindutin upang matuto nang higit pa."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Na-disable ang <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Conference Call"</string>
</resources>
diff --git a/core/res/res/values-tr-watch/styles_material.xml b/core/res/res/values-tr-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-tr-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index a1f6f64..5a0776a 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Doğru!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Tekrar deneyin"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Tekrar deneyin"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Tüm özellikler ve veriler için kilidi açın"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Yüz Tanıma Kilidi için maksimum deneme sayısı aşıldı"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"SIM kart yok"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Tablette SIM kart yok."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android yeni sürüme geçiriliyor..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android başlatılıyor…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Depolama optimize ediliyor."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android yeni sürüme geçiriliyor"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android güncellemesi tamamlanıyor…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Yeni sürüme geçiş işlemi tamamlanana kadar bazı uygulamalar düzgün çalışmayabilir"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> yeni sürüme geçiriliyor…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g> uygulama optimize ediliyor."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> hazırlanıyor."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Uygulamalar başlatılıyor"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Sabitlemeyi kaldır"</string>
<string name="app_info" msgid="6856026610594615344">"Uygulama bilgileri"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Cihaz sıfırlansın mı?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Cihazı sıfırlamak için dokunun"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Demo başlatılıyor…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Cihaz sıfırlanıyor…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Cihaz sıfırlansın mı?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Değişiklikleri kaybedeceksiniz ve demo <xliff:g id="TIMEOUT">%1$s</xliff:g> saniye içinde tekrar başlayacak…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"İptal"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Şimdi sıfırla"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Bu cihazı kısıtlama olmadan kullanmak için fabrika ayarlarına sıfırlayın"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Daha fazla bilgi edinmek için dokunun."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> devre dışı"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Konferans Çağrısı"</string>
</resources>
diff --git a/core/res/res/values-uk-watch/styles_material.xml b/core/res/res/values-uk-watch/styles_material.xml
new file mode 100644
index 0000000..698d5b0
--- /dev/null
+++ b/core/res/res/values-uk-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"варіанти"</font></string>
+</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index c8b3754..4810e37 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -684,6 +684,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Правильно!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Повторіть спробу"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Повторіть спробу"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Розблокуйте, щоб бачити всі функції й дані"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Перевищено максимальну кількість спроб розблокування за допомогою функції \"Фейсконтроль\""</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Відсутня SIM-карта"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"У пристр. нема SIM-карти."</string>
@@ -1068,8 +1069,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android оновлюється..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Запуск ОС Android…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Оптимізація пам’яті."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android оновлюється"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Завершується оновлення Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Деякі додатки можуть не працювати належним чином, доки не завершиться оновлення"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"Додаток <xliff:g id="APPLICATION">%1$s</xliff:g> оновлюється…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Оптимізація програми <xliff:g id="NUMBER_0">%1$d</xliff:g> з <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Підготовка додатка <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Запуск програм."</string>
@@ -1723,7 +1725,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Відкріпити"</string>
<string name="app_info" msgid="6856026610594615344">"Про додаток"</string>
<string name="negative_duration" msgid="5688706061127375131">"-<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Скинути налаштування пристрою?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Торкніться, щоб скинути налаштування пристрою"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Запуск демонстрації…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Скидання налаштувань пристрою…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Скинути налаштування пристрою?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Ви втратите всі зміни, а демонстрація знову почнеться через <xliff:g id="TIMEOUT">%1$s</xliff:g> с…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Скасувати"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Скинути"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Відновіть заводські параметри, щоб використовувати пристрій без обмежень"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Торкніться, щоб дізнатися більше."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> вимкнено"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Конференц-виклик"</string>
</resources>
diff --git a/core/res/res/values-ur-rPK-watch/styles_material.xml b/core/res/res/values-ur-rPK-watch/styles_material.xml
new file mode 100644
index 0000000..e569c7c
--- /dev/null
+++ b/core/res/res/values-ur-rPK-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"امیدواران"</font></string>
+</resources>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index 91a8c06..f677132 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"صحیح!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"دوبارہ کوشش کریں"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"دوبارہ کوشش کریں"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"تمام خصوصیات اور ڈیٹا کیلئے غیر مقفل کریں"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"چہرہ کے ذریعے غیر مقفل کریں کی زیادہ سے زیادہ کوششوں سے تجاوز کرگیا"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"کوئی SIM کارڈ نہیں ہے"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"ٹیبلیٹ میں کوئی SIM کارڈ نہیں ہے۔"</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android اپ گریڈ ہو رہا ہے…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android شروع ہو رہا ہے…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"اسٹوریج کو بہترین بنایا جا رہا ہے۔"</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android اپ گریڈ ہو رہا ہے"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android اپ ڈیٹ ختم ہو رہی ہے…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"اپ گریڈ ختم ہونے تک شاید کچھ ایپس ٹھیک طرح سے کام نہ کریں"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> اپ گریڈ ہو رہی ہے…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"ایپ <xliff:g id="NUMBER_0">%1$d</xliff:g> از <xliff:g id="NUMBER_1">%2$d</xliff:g> کو بہتر بنایا جا رہا ہے۔"</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> تیار ہو رہی ہے۔"</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"ایپس شروع ہو رہی ہیں۔"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"پن ہٹائیں"</string>
<string name="app_info" msgid="6856026610594615344">"ایپ کی معلومات"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"آلہ ری سیٹ کریں؟"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"آلہ ری سیٹ کرنے کیلئے تھپتھپائیں"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"ڈیمو شروع ہو رہا ہے…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"آلہ ری سیٹ ہو رہا ہے…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"آلہ ری سیٹ کریں؟"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"آپ کی تمام تبدیلیاں ضائع ہو جائیں گی اور ڈیمو <xliff:g id="TIMEOUT">%1$s</xliff:g> سیکنڈز میں دوبارہ شروع ہوگا…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"منسوخ کریں"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"ابھی ری سیٹ کریں"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"بغیر کسی حدود کے استعمال کرنے کیلئے اس آلے کو فیکٹری ری سیٹ کریں"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"مزید جاننے کیلئے ٹچ کریں۔"</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"غیر فعال کردہ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"کانفرنس کال"</string>
</resources>
diff --git a/core/res/res/values-uz-rUZ-watch/styles_material.xml b/core/res/res/values-uz-rUZ-watch/styles_material.xml
new file mode 100644
index 0000000..3cb38d6
--- /dev/null
+++ b/core/res/res/values-uz-rUZ-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"nomzodlar"</font></string>
+</resources>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index 32a1328..8fc6428 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -44,7 +44,7 @@
<string name="unknownName" msgid="6867811765370350269">"Noma’lum"</string>
<string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"Ovozli pochta"</string>
<string name="defaultMsisdnAlphaTag" msgid="2850889754919584674">"MSISDN1"</string>
- <string name="mmiError" msgid="5154499457739052907">"Ulanishda xato yoki noto‘g‘ri MMI kodi."</string>
+ <string name="mmiError" msgid="5154499457739052907">"Tarmoqda xato yoki MMI kod noto‘g‘ri."</string>
<string name="mmiFdnError" msgid="5224398216385316471">"Bu amal faqat ruxsat etilgan raqamlar uchun mavjud."</string>
<string name="serviceEnabled" msgid="8147278346414714315">"Xizmat yoqildi."</string>
<string name="serviceEnabledFor" msgid="6856228140453471041">"Xizmat quyidagi uchun yoqildi:"</string>
@@ -143,7 +143,7 @@
<string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Yo‘naltirilmadi"</string>
<string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: yo‘naltirilmadi"</string>
<string name="fcComplete" msgid="3118848230966886575">"Maxsus kod bajarildi."</string>
- <string name="fcError" msgid="3327560126588500777">"Ulanishda muammo yoki maxsus kod xato."</string>
+ <string name="fcError" msgid="3327560126588500777">"Tarmoqda xato yoki maxsus kod noto‘g‘ri."</string>
<string name="httpErrorOk" msgid="1191919378083472204">"OK"</string>
<string name="httpError" msgid="7956392511146698522">"Tarmoqda xato yuz berdi."</string>
<string name="httpErrorLookup" msgid="4711687456111963163">"URL topilmadi."</string>
@@ -245,7 +245,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktlar"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"kontaktlarga kirish"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Joylashuv"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"shu qurilmaning joylashuvi haqidagi ma’lumotlarga kirish"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"shu qurilmaning joylashuvi haqidagi axborotga kirish"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Taqvim"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"taqvimingizga kirish"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"To‘g‘ri!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Qaytadan urining"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Qaytadan urining"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Barcha funksiya va ma’lumotlar uchun qulfdan chiqaring"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Yuzni tanitib qulfni ochishga urinish miqdoridan oshib ketdi"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"SIM karta yo‘q"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Planshetingizda SIM karta yo‘q."</string>
@@ -985,7 +986,7 @@
<string name="whichImageCaptureApplicationNamed" msgid="8619384150737825003">"%1$s yordamida suratga oling"</string>
<string name="whichImageCaptureApplicationLabel" msgid="6390303445371527066">"Suratga olish"</string>
<string name="alwaysUse" msgid="4583018368000610438">"Ushbu amaldan standart sifatida foydalanish"</string>
- <string name="use_a_different_app" msgid="8134926230585710243">"Boshqa ilovadan foydalanish"</string>
+ <string name="use_a_different_app" msgid="8134926230585710243">"Boshqa ilova"</string>
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"Birlamchi sozlamalarni Tizim sozlamalari > Ilovalar > Yuklab olingan menyusidan tozalang."</string>
<string name="chooseActivity" msgid="7486876147751803333">"Amalni tanlash"</string>
<string name="chooseUsbActivity" msgid="6894748416073583509">"USB qurilma uchun ilovani tanlang"</string>
@@ -998,16 +999,16 @@
<string name="aerr_report" msgid="5371800241488400617">"Fikr-mulohaza yuborish"</string>
<string name="aerr_close" msgid="2991640326563991340">"Yopish"</string>
<string name="aerr_mute" msgid="1974781923723235953">"Qurilma o‘chib yonguncha e’tiborsiz qoldirish"</string>
- <string name="aerr_wait" msgid="3199956902437040261">"Kuting"</string>
+ <string name="aerr_wait" msgid="3199956902437040261">"Kutish"</string>
<string name="aerr_close_app" msgid="3269334853724920302">"Ilovani yopish"</string>
<string name="anr_title" msgid="4351948481459135709"></string>
- <string name="anr_activity_application" msgid="8493290105678066167">"<xliff:g id="APPLICATION">%2$s</xliff:g> javob bermayapti"</string>
- <string name="anr_activity_process" msgid="1622382268908620314">"<xliff:g id="ACTIVITY">%1$s</xliff:g> javob bermayapti"</string>
- <string name="anr_application_process" msgid="6417199034861140083">"<xliff:g id="APPLICATION">%1$s</xliff:g> javob bermayapti"</string>
+ <string name="anr_activity_application" msgid="8493290105678066167">"<xliff:g id="APPLICATION">%2$s</xliff:g> ilovasi javob bermayapti"</string>
+ <string name="anr_activity_process" msgid="1622382268908620314">"<xliff:g id="ACTIVITY">%1$s</xliff:g> harakati javob bermayapti"</string>
+ <string name="anr_application_process" msgid="6417199034861140083">"<xliff:g id="APPLICATION">%1$s</xliff:g> ilovasi javob bermayapti"</string>
<string name="anr_process" msgid="6156880875555921105">"<xliff:g id="PROCESS">%1$s</xliff:g> ilovasi javob bermayapti"</string>
<string name="force_close" msgid="8346072094521265605">"OK"</string>
<string name="report" msgid="4060218260984795706">"Xabar berish"</string>
- <string name="wait" msgid="7147118217226317732">"Kuting"</string>
+ <string name="wait" msgid="7147118217226317732">"Kutish"</string>
<string name="webpage_unresponsive" msgid="3272758351138122503">"Sahifa javob bermayapti.\n\nUni yopishni xohlaysizmi?"</string>
<string name="launch_warning_title" msgid="1547997780506713581">"Ilova qayta yo‘naltirildi"</string>
<string name="launch_warning_replace" msgid="6202498949970281412">"<xliff:g id="APP_NAME">%1$s</xliff:g> hozirda ishlamoqda"</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android yangilanmoqda…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android ishga tushmoqda…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Xotira optimallashtirilmoqda."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android yangilanmoqda"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Android yangilanishi tugay deb qoldi…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Yangilanish vaqtida ba’zi ilovalar to‘g‘ri ishlamasligi mumkin"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> ilovasi yangilanmoqda…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Ilovalar optimallashtirilmoqda (<xliff:g id="NUMBER_0">%1$d</xliff:g> / <xliff:g id="NUMBER_1">%2$d</xliff:g>)."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> tayyorlanmoqda."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Ilovalar ishga tushirilmoqda."</string>
@@ -1315,7 +1317,7 @@
<string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB xotira qurilmasi"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB xotira"</string>
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Tahrirlash"</string>
- <string name="data_usage_warning_title" msgid="1955638862122232342">"Ma’lumotlardan foydalanish ogohlantirilishi"</string>
+ <string name="data_usage_warning_title" msgid="1955638862122232342">"Trafik kam qoldi"</string>
<string name="data_usage_warning_body" msgid="6660692274311972007">"Trafik sarfi va sozlamalarni ko‘rish uchun bosing."</string>
<string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G trafik chekloviga yetdi"</string>
<string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G trafik chekloviga yetdi"</string>
@@ -1350,7 +1352,7 @@
<string name="launchBrowserDefault" msgid="2057951947297614725">"Brauzer ishga tushirilsinmi?"</string>
<string name="SetupCallDefault" msgid="5834948469253758575">"Qo‘ng‘iroqni qabul qilasizmi?"</string>
<string name="activity_resolver_use_always" msgid="8017770747801494933">"Har doim"</string>
- <string name="activity_resolver_use_once" msgid="2404644797149173758">"Bir marta"</string>
+ <string name="activity_resolver_use_once" msgid="2404644797149173758">"Faqat hozir"</string>
<string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"“%1$s” ishchi profilni qo‘llab-quvvatlamaydi"</string>
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Planshet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Olib tashlash"</string>
<string name="app_info" msgid="6856026610594615344">"Ilova haqida"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Qurilma asl holatga qaytarilsinmi?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Qurilmani asl holatga qaytarish uchun bosing"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Demo boshlanmoqda…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Qurilma asl holatga qaytarilmoqda…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Qurilma asl holatga qaytarilsinmi?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Har qanday o‘zgarishlar o‘chib ketadi va demo <xliff:g id="TIMEOUT">%1$s</xliff:g> soniyadan so‘ng yana qayta ishga tushadi…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Bekor qilish"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Asl holatga qaytarish"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Bu qurilmadan cheklovlarsiz foydalanish uchun zavod sozlamalarini tiklang"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Ko‘proq o‘rganish uchun bosing."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> vidjeti o‘chirilgan"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Konferens-aloqa"</string>
</resources>
diff --git a/core/res/res/values-vi-watch/styles_material.xml b/core/res/res/values-vi-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-vi-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 3b86a37..a537b8f 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Chính xác!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Thử lại"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Thử lại"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Mở khóa đối với tất cả các tính năng và dữ liệu"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Đã vượt quá số lần Mở khóa bằng khuôn mặt tối đa"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Không có thẻ SIM nào"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Không có thẻ SIM nào trong máy tính bảng."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android đang nâng cấp..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android đang khởi động..."</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Tối ưu hóa lưu trữ."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android đang nâng cấp"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Hoàn tất cập nhật Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Một số ứng dụng có thể không hoạt động bình thường cho đến khi nâng cấp xong"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> đang nâng cấp…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Đang tối ưu hóa ứng dụng <xliff:g id="NUMBER_0">%1$d</xliff:g> trong tổng số <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Đang chuẩn bị <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Khởi động ứng dụng."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Bỏ ghim"</string>
<string name="app_info" msgid="6856026610594615344">"Thông tin ứng dụng"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Đặt lại thiết bị?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Nhấn để đặt lại thiết bị"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Đang bắt đầu bản trình diễn..."</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Đang đặt lại thiết bị..."</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Đặt lại thiết bị?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Bạn sẽ bị mất mọi thay đổi và bản trình diễn sẽ bắt đầu lại sau <xliff:g id="TIMEOUT">%1$s</xliff:g> giây…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Hủy"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Đặt lại ngay bây giờ"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Khôi phục cài đặt gốc để sử dụng thiết bị này mà không bị hạn chế"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Chạm để tìm hiểu thêm."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"Đã tắt <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Cuộc gọi nhiều bên"</string>
</resources>
diff --git a/packages/DocumentsUI/res/values/attrs.xml b/core/res/res/values-w192dp/dimens_material.xml
similarity index 74%
copy from packages/DocumentsUI/res/values/attrs.xml
copy to core/res/res/values-w192dp/dimens_material.xml
index 9e13001..797bf5a 100644
--- a/packages/DocumentsUI/res/values/attrs.xml
+++ b/core/res/res/values-w192dp/dimens_material.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- 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.
@@ -14,7 +14,7 @@
limitations under the License.
-->
<resources>
- <declare-styleable name="DocumentsTheme">
- <attr name="colorActionMode" format="color"/>
- </declare-styleable>
+ <dimen name="screen_percentage_05">9.6dp</dimen>
+ <dimen name="screen_percentage_10">19.2dp</dimen>
+ <dimen name="screen_percentage_15">28.8dp</dimen>
</resources>
diff --git a/packages/DocumentsUI/res/values/attrs.xml b/core/res/res/values-w205dp/dimens_material.xml
similarity index 73%
copy from packages/DocumentsUI/res/values/attrs.xml
copy to core/res/res/values-w205dp/dimens_material.xml
index 9e13001..94907ee 100644
--- a/packages/DocumentsUI/res/values/attrs.xml
+++ b/core/res/res/values-w205dp/dimens_material.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- 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.
@@ -14,7 +14,7 @@
limitations under the License.
-->
<resources>
- <declare-styleable name="DocumentsTheme">
- <attr name="colorActionMode" format="color"/>
- </declare-styleable>
+ <dimen name="screen_percentage_05">10.25dp</dimen>
+ <dimen name="screen_percentage_10">20.5dp</dimen>
+ <dimen name="screen_percentage_15">30.75dp</dimen>
</resources>
diff --git a/packages/DocumentsUI/res/values/attrs.xml b/core/res/res/values-w213dp/dimens_material.xml
similarity index 73%
copy from packages/DocumentsUI/res/values/attrs.xml
copy to core/res/res/values-w213dp/dimens_material.xml
index 9e13001..8a4e3a0 100644
--- a/packages/DocumentsUI/res/values/attrs.xml
+++ b/core/res/res/values-w213dp/dimens_material.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- 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.
@@ -14,7 +14,7 @@
limitations under the License.
-->
<resources>
- <declare-styleable name="DocumentsTheme">
- <attr name="colorActionMode" format="color"/>
- </declare-styleable>
+ <dimen name="screen_percentage_05">10.65dp</dimen>
+ <dimen name="screen_percentage_10">21.3dp</dimen>
+ <dimen name="screen_percentage_15">31.95dp</dimen>
</resources>
diff --git a/packages/DocumentsUI/res/values/attrs.xml b/core/res/res/values-w228dp/dimens_material.xml
similarity index 73%
copy from packages/DocumentsUI/res/values/attrs.xml
copy to core/res/res/values-w228dp/dimens_material.xml
index 9e13001..a200975 100644
--- a/packages/DocumentsUI/res/values/attrs.xml
+++ b/core/res/res/values-w228dp/dimens_material.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- 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.
@@ -14,7 +14,7 @@
limitations under the License.
-->
<resources>
- <declare-styleable name="DocumentsTheme">
- <attr name="colorActionMode" format="color"/>
- </declare-styleable>
+ <dimen name="screen_percentage_05">11.4dp</dimen>
+ <dimen name="screen_percentage_10">22.8dp</dimen>
+ <dimen name="screen_percentage_15">34.2dp</dimen>
</resources>
diff --git a/packages/DocumentsUI/res/values/attrs.xml b/core/res/res/values-w240dp/dimens_material.xml
similarity index 74%
copy from packages/DocumentsUI/res/values/attrs.xml
copy to core/res/res/values-w240dp/dimens_material.xml
index 9e13001..a4b58fa 100644
--- a/packages/DocumentsUI/res/values/attrs.xml
+++ b/core/res/res/values-w240dp/dimens_material.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- 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.
@@ -14,7 +14,7 @@
limitations under the License.
-->
<resources>
- <declare-styleable name="DocumentsTheme">
- <attr name="colorActionMode" format="color"/>
- </declare-styleable>
+ <dimen name="screen_percentage_05">12dp</dimen>
+ <dimen name="screen_percentage_10">24dp</dimen>
+ <dimen name="screen_percentage_15">36dp</dimen>
</resources>
diff --git a/core/res/res/values-watch/colors_material.xml b/core/res/res/values-watch/colors_material.xml
new file mode 100644
index 0000000..45eb981
--- /dev/null
+++ b/core/res/res/values-watch/colors_material.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<resources>
+ <color name="background_material_dark">#ff232e33</color>
+ <color name="background_floating_material_dark">#ff3e5059</color>
+
+ <color name="accent_material_dark">#ff5e97f6</color>
+ <color name="accent_material_light">#ff4285f4</color>
+
+ <color name="primary_material_dark">#4D4D4D</color>
+
+ <color name="button_material_dark">#ff999999</color>
+</resources>
diff --git a/core/res/res/values-watch/config_material.xml b/core/res/res/values-watch/config_material.xml
new file mode 100644
index 0000000..81b53e7
--- /dev/null
+++ b/core/res/res/values-watch/config_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for watch products. Do not translate. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Watch type devices have limited screen real-estate, and thus action bars should not be
+ used. -->
+ <bool name="config_windowActionBarSupported">false</bool>
+
+ <!-- Watch type devices have limited screen real-estate, and thus titles should not be used. -->
+ <bool name="config_windowNoTitleDefault">true</bool>
+
+ <!-- Use micro alert controller -->
+ <integer name="config_alertDialogController">1</integer>
+
+ <!-- Always overscan by default to ensure onApplyWindowInsets will always be called. -->
+ <bool name="config_windowOverscanByDefault">true</bool>
+
+ <!-- Due to the smaller screen size, have dialog titles occupy more than 1 line. -->
+ <integer name="config_dialogWindowTitleMaxLines">3</integer>
+</resources>
diff --git a/packages/DocumentsUI/res/values/attrs.xml b/core/res/res/values-watch/dimens_material.xml
similarity index 77%
rename from packages/DocumentsUI/res/values/attrs.xml
rename to core/res/res/values-watch/dimens_material.xml
index 9e13001..d579434 100644
--- a/packages/DocumentsUI/res/values/attrs.xml
+++ b/core/res/res/values-watch/dimens_material.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- 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.
@@ -14,7 +14,5 @@
limitations under the License.
-->
<resources>
- <declare-styleable name="DocumentsTheme">
- <attr name="colorActionMode" format="color"/>
- </declare-styleable>
+ <item name="text_line_spacing_multiplier_material" format="float" type="dimen">1.2</item>
</resources>
diff --git a/core/res/res/values-watch/donottranslate_material.xml b/core/res/res/values-watch/donottranslate_material.xml
new file mode 100644
index 0000000..a6f2ff4
--- /dev/null
+++ b/core/res/res/values-watch/donottranslate_material.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+ <string name="font_family_display_4_material">sans-serif-condensed-light</string>
+ <string name="font_family_display_3_material">sans-serif-condensed-light</string>
+ <string name="font_family_display_2_material">sans-serif-condensed-light</string>
+ <string name="font_family_display_1_material">sans-serif-condensed-light</string>
+ <string name="font_family_headline_material">sans-serif-condensed-light</string>
+ <string name="font_family_title_material">sans-serif-condensed</string>
+ <string name="font_family_subhead_material">sans-serif-condensed-light</string>
+ <string name="font_family_menu_material">sans-serif-condensed-light</string>
+ <string name="font_family_body_2_material">sans-serif-condensed</string>
+ <string name="font_family_body_1_material">sans-serif-condensed-light</string>
+ <string name="font_family_caption_material">sans-serif-condensed-light</string>
+ <string name="font_family_button_material">sans-serif-condensed</string>
+ </resources>
diff --git a/core/res/res/values-watch/styles_material.xml b/core/res/res/values-watch/styles_material.xml
new file mode 100644
index 0000000..f5735e6
--- /dev/null
+++ b/core/res/res/values-watch/styles_material.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+<resources>
+ <style name="Animation.Material.Activity" parent="Animation.Activity">
+ <item name="activityOpenEnterAnimation">@anim/slide_in_enter_micro</item>
+ <item name="activityOpenRemoteViewsEnterAnimation">@anim/slide_in_enter_micro</item>
+ <item name="activityOpenExitAnimation">@anim/slide_in_exit_micro</item>
+ <item name="activityCloseEnterAnimation">@null</item>
+ <item name="activityCloseExitAnimation">@anim/slide_out_micro</item>
+ <item name="taskOpenEnterAnimation">@anim/slide_in_enter_micro</item>
+ <item name="taskOpenExitAnimation">@anim/slide_in_exit_micro</item>
+ <item name="taskCloseEnterAnimation">@null</item>
+ <item name="taskCloseExitAnimation">@anim/slide_out_micro</item>
+ <item name="taskToFrontEnterAnimation">@anim/slide_in_enter_micro</item>
+ <item name="taskToFrontExitAnimation">@anim/slide_in_exit_micro</item>
+ <item name="taskToBackEnterAnimation">@null</item>
+ <item name="taskToBackExitAnimation">@anim/slide_out_micro</item>
+ <item name="wallpaperOpenEnterAnimation">@null</item>
+ <item name="wallpaperOpenExitAnimation">@anim/slide_out_micro</item>
+ <item name="wallpaperCloseEnterAnimation">@anim/slide_in_enter_micro</item>
+ <item name="wallpaperCloseExitAnimation">@anim/slide_in_exit_micro</item>
+ <item name="wallpaperIntraOpenEnterAnimation">@null</item>
+ <item name="wallpaperIntraOpenExitAnimation">@anim/slide_out_micro</item>
+ <item name="wallpaperIntraCloseEnterAnimation">@anim/slide_in_enter_micro</item>
+ <item name="wallpaperIntraCloseExitAnimation">@anim/slide_in_exit_micro</item>
+ </style>
+
+ <style name="PreferenceFragment.Material" parent="BasePreferenceFragment">
+ <item name="divider">@empty</item>
+ </style>
+
+ <style name="Preference.Material.PreferenceScreen" parent="Preference.Material.BasePreferenceScreen">
+ <item name="divider">@empty</item>
+ </style>
+
+ <style name="Widget.Material.TextView" parent="Widget.TextView">
+ <item name="breakStrategy">balanced</item>
+ </style>
+
+ <style name="Widget.Material.ButtonBar" parent="Widget.Material.BaseButtonBar" />
+
+ <!-- Alert dialog button bar button -->
+ <style name="Widget.Material.Button.ButtonBar.AlertDialog" parent="Widget.Material.Button.Borderless.Small">
+ <item name="paddingStart">@dimen/list_item_padding_start_material</item>
+ <item name="paddingEnd">@dimen/list_item_padding_end_material</item>
+ <item name="drawablePadding">8dp</item>
+ <item name="gravity">center_vertical|left</item>
+ <item name="minWidth">64dp</item>
+ <item name="minHeight">@dimen/alert_dialog_button_bar_height</item>
+ </style>
+
+ <style name="Widget.Material.NumberPicker" parent="Widget.NumberPicker">
+ <item name="internalLayout">@layout/number_picker_material</item>
+ <item name="solidColor">@color/transparent</item>
+ <item name="selectionDivider">@drawable/numberpicker_selection_divider</item>
+ <item name="selectionDividerHeight">2dp</item>
+ <item name="selectionDividersDistance">48dp</item>
+ <item name="internalMinWidth">64dp</item>
+ <item name="internalMaxHeight">180dp</item>
+ <item name="virtualButtonPressedDrawable">?selectableItemBackground</item>
+ <item name="descendantFocusability">blocksDescendants</item>
+ </style>
+
+ <!-- DO NOTE TRANSLATE Spans within this text are applied to style composing regions
+ within an EditText widget. The text content is ignored and not used.
+ Note: This is @color/material_deep_teal_200, cannot use @color references here. -->
+ <string name="candidates_style"><font color="#80cbc4">candidates</font></string>
+</resources>
diff --git a/core/res/res/values-watch/themes.xml b/core/res/res/values-watch/themes.xml
index 6d6065f..04df180 100644
--- a/core/res/res/values-watch/themes.xml
+++ b/core/res/res/values-watch/themes.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
@@ -14,11 +14,10 @@
limitations under the License.
-->
<resources>
- <style name="Theme.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
- <style name="Theme.Dialog.AppError" parent="Theme.Micro.Dialog.AppError" />
- <style name="Theme.Holo.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
- <style name="Theme.Holo.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
- <style name="Theme.InputMethod" parent="Theme.Micro.InputMethod" />
- <style name="Theme.Material.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
- <style name="Theme.Material.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
+ <!-- Theme for the dialog shown when an app crashes or ANRs. Override to make it dark. -->
+ <style name="Theme.Dialog.AppError" parent="Theme.DeviceDefault.Dialog.Alert">
+ <item name="windowContentTransitions">false</item>
+ <item name="windowActivityTransitions">false</item>
+ <item name="windowCloseOnTouchOutside">false</item>
+ </style>
</resources>
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index 66509fb..2313b26 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -13,22 +13,31 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources>
- <style name="Theme.DeviceDefault" parent="Theme.Micro" />
- <style name="Theme.DeviceDefault.NoActionBar" parent="Theme.Micro" />
- <style name="Theme.DeviceDefault.Dialog" parent="Theme.Micro.Dialog" />
- <style name="Theme.DeviceDefault.DialogWhenLarge" parent="Theme.Micro.Dialog" />
- <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
- <style name="Theme.DeviceDefault.InputMethod" parent="Theme.Micro.InputMethod" />
- <style name="Theme.DeviceDefault.Panel" parent="Theme.Micro.Panel" />
- <style name="Theme.DeviceDefault.Light" parent="Theme.Micro.Light" />
- <style name="Theme.DeviceDefault.Light.NoActionBar" parent="Theme.Micro.Light" />
- <style name="Theme.DeviceDefault.Light.DarkActionBar" parent="Theme.Micro.Light" />
- <style name="Theme.DeviceDefault.Light.Dialog" parent="Theme.Micro.Dialog" />
- <style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="Theme.Micro.Dialog" />
- <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
- <style name="Theme.DeviceDefault.Light.Panel" parent="Theme.Micro.Light.Panel" />
- <style name="Theme.DeviceDefault.Settings" parent="Theme.Micro" />
- <style name="Theme.DeviceDefault.Wallpaper" parent="Theme.Micro" />
-</resources>
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+This file contains the themes that are the Device Defaults.
+If you want to edit themes to skin your device, do it here.
+We recommend that you do not edit themes.xml and instead edit
+this file.
+
+Editing this file instead of themes.xml will greatly simplify
+merges for future platform versions and CTS compliance will be
+easier.
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+<resources>
+ <!-- Theme used for the intent picker activity. -->
+ <style name="Theme.DeviceDefault.Resolver" parent="Theme.Material">
+ <item name="colorControlActivated">?attr/colorControlHighlight</item>
+ <item name="listPreferredItemPaddingStart">?attr/dialogPreferredPadding</item>
+ <item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item>
+ </style>
+
+ <!-- Use a dark theme for watches. -->
+ <style name="Theme.DeviceDefault.System" parent="Theme.Material" />
+</resources>
diff --git a/core/res/res/values-watch/themes_material.xml b/core/res/res/values-watch/themes_material.xml
new file mode 100644
index 0000000..4ae4367
--- /dev/null
+++ b/core/res/res/values-watch/themes_material.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+<resources>
+ <!-- Default theme for material style input methods, which is used by the
+ {@link android.inputmethodservice.InputMethodService} class.
+ this inherits from Theme.Panel, but sets up IME appropriate animations
+ and a few custom attributes. -->
+ <style name="Theme.Material.InputMethod" parent="Theme.Material.Panel">
+ <item name="windowAnimationStyle">@style/Animation.InputMethod</item>
+ <item name="imeFullscreenBackground">?colorBackground</item>
+ <item name="imeExtractEnterAnimation">@anim/input_method_extract_enter</item>
+ </style>
+
+ <!-- Override behaviour to set the theme colours for dialogs, keep them the same. -->
+ <style name="ThemeOverlay.Material.Dialog" parent="ThemeOverlay.Material.BaseDialog">
+ <item name="windowIsFloating">false</item>
+ </style>
+
+ <!-- Force the background and floating colours to be the default colours. -->
+ <style name="Theme.Material.Dialog" parent="Theme.Material.BaseDialog">
+ <item name="colorBackground">@color/background_material_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_material_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_dark</item>
+ <item name="windowIsFloating">false</item>
+ </style>
+
+ <!-- Force the background and floating colours to be the default colours. -->
+ <style name="Theme.Material.Light.Dialog" parent="Theme.Material.Light.BaseDialog">
+ <item name="colorBackground">@color/background_material_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_material_light</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_light</item>
+ <item name="windowIsFloating">false</item>
+ </style>
+</resources>
diff --git a/core/res/res/values-zh-rCN-watch/styles_material.xml b/core/res/res/values-zh-rCN-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-zh-rCN-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index d7490c5..8116ec0 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -310,7 +310,7 @@
<string name="permdesc_enableCarMode" msgid="4853187425751419467">"允许应用启用车载模式。"</string>
<string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"关闭其他应用"</string>
<string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"允许该应用结束其他应用的后台进程。此权限可导致其他应用停止运行。"</string>
- <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"在其他应用之上显示内容"</string>
+ <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"出现在其他应用上"</string>
<string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"允许该应用在其他应用之上或用户界面的特定部分绘图。这可能会干扰您对所有应用界面的使用,或使您在其他应用中看到的内容发生变化。"</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"让应用始终运行"</string>
<string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"允许该应用在内存中持续保留其自身的某些组件。这会限制其他应用可用的内存,从而减缓平板电脑运行速度。"</string>
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"正确!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"重试"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"重试"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"解锁即可使用所有功能和数据"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"已超过“人脸解锁”尝试次数上限"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"没有 SIM 卡"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"平板电脑中没有SIM卡。"</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"Android正在升级..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android 正在启动…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"正在优化存储空间。"</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"Android 正在升级"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"即将完成 Android 更新…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"在升级完成之前,部分应用可能无法正常运行"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"正在升级<xliff:g id="APPLICATION">%1$s</xliff:g>…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"正在优化第<xliff:g id="NUMBER_0">%1$d</xliff:g>个应用(共<xliff:g id="NUMBER_1">%2$d</xliff:g>个)。"</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"正在准备升级<xliff:g id="APPNAME">%1$s</xliff:g>。"</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"正在启动应用。"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"取消固定"</string>
<string name="app_info" msgid="6856026610594615344">"应用信息"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"要重置设备吗?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"点按即可重置设备"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"正在启动演示模式…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"正在重置设备…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"要重置设备吗?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"您将丢失所有更改,而且演示模式将在 <xliff:g id="TIMEOUT">%1$s</xliff:g> 秒后重新启动…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"取消"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"立即重置"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"恢复出厂设置即可正常使用此设备,不受任何限制"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"触摸即可了解详情。"</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"已停用的<xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"电话会议"</string>
</resources>
diff --git a/core/res/res/values-zh-rHK-watch/styles_material.xml b/core/res/res/values-zh-rHK-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-zh-rHK-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 6c896b2..ef12eea 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"正確!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"再試一次"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"再試一次"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"解鎖即可使用所有功能和資料"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"已超過臉容解鎖嘗試次數上限"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"找不到 SIM 卡"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"平板電腦中沒有 SIM 卡。"</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"正在升級 Android..."</string>
<string name="android_start_title" msgid="8418054686415318207">"Android 正在啟動…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"正在優化儲存空間。"</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"正在升級 Android"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"正在完成 Android 更新…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"部分應用程式需要完成升級方可正常運作"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"「<xliff:g id="APPLICATION">%1$s</xliff:g>」正在升級…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"正在優化第 <xliff:g id="NUMBER_0">%1$d</xliff:g> 個應用程式 (共 <xliff:g id="NUMBER_1">%2$d</xliff:g> 個)。"</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"正在準備 <xliff:g id="APPNAME">%1$s</xliff:g>。"</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"正在啟動應用程式。"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"取消固定"</string>
<string name="app_info" msgid="6856026610594615344">"應用程式資料"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"要重設裝置嗎?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"輕按即可重設裝置"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"正在開始示範…"</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"正在重設裝置…"</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"要重設裝置嗎?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"系統將不會儲存變更,示範將於 <xliff:g id="TIMEOUT">%1$s</xliff:g> 秒後重新開始…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"取消"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"立即重設"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"將此裝置回復至原廠設定後,使用將不受限制"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"輕觸以瞭解詳情。"</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"「<xliff:g id="LABEL">%1$s</xliff:g>」已停用"</string>
+ <string name="conference_call" msgid="3751093130790472426">"會議通話"</string>
</resources>
diff --git a/core/res/res/values-zh-rTW-watch/styles_material.xml b/core/res/res/values-zh-rTW-watch/styles_material.xml
new file mode 100644
index 0000000..36a459d
--- /dev/null
+++ b/core/res/res/values-zh-rTW-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"candidates"</font></string>
+</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index f3691cd..84e2c2c 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"正確!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"再試一次"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"再試一次"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"解鎖即可使用所有功能和資料"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"已超過人臉解鎖嘗試次數上限"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"找不到 SIM 卡"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"平板電腦中沒有 SIM 卡。"</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"正在升級 Android…"</string>
<string name="android_start_title" msgid="8418054686415318207">"Android 正在啟動…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"正在對儲存空間進行最佳化處理。"</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"正在升級 Android"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"即將完成 Android 更新…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"升級完成前,部分應用程式可能無法正常運作"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"正在升級「<xliff:g id="APPLICATION">%1$s</xliff:g>」…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"正在最佳化第 <xliff:g id="NUMBER_0">%1$d</xliff:g> 個應用程式 (共 <xliff:g id="NUMBER_1">%2$d</xliff:g> 個)。"</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"正在準備升級「<xliff:g id="APPNAME">%1$s</xliff:g>」。"</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"正在啟動應用程式。"</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"取消固定"</string>
<string name="app_info" msgid="6856026610594615344">"應用程式資訊"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"要重設裝置嗎?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"輕觸即可重設裝置"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"正在啟動示範模式..."</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"正在重設裝置..."</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"要重設裝置嗎?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"系統不會儲存您所做的變更,示範模式將於 <xliff:g id="TIMEOUT">%1$s</xliff:g> 秒後重新開始…"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"取消"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"立即重設"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"恢復原廠設定即可正常使用這個裝置"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"輕觸即可瞭解詳情。"</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"已停用的<xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="conference_call" msgid="3751093130790472426">"電話會議"</string>
</resources>
diff --git a/core/res/res/values-zu-watch/styles_material.xml b/core/res/res/values-zu-watch/styles_material.xml
new file mode 100644
index 0000000..8b69fef
--- /dev/null
+++ b/core/res/res/values-zu-watch/styles_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="candidates_style" msgid="8052530148128607468"><font color="#80cbc4">"amakhandidethi"</font></string>
+</resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index a2f5c13..0c7af8d 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -678,6 +678,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Lungile!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Zama futhi"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Zama futhi"</string>
+ <string name="lockscreen_storage_locked" msgid="9167551160010625200">"Vulela zonke izici nedatha"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Ukuzama Kokuvula Ubuso Okuningi kudluliwe"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Alikho ikhadi le-SIM."</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Alikho ikhadi le-SIM efonini."</string>
@@ -1022,8 +1023,9 @@
<string name="android_upgrading_title" msgid="1584192285441405746">"I-Android ifaka ezakamuva..."</string>
<string name="android_start_title" msgid="8418054686415318207">"I-Android iyaqala…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Ikhulisa isitoreji."</string>
- <string name="android_upgrading_notification_title" msgid="1619393112444671028">"I-Android iyathuthukiswa"</string>
+ <string name="android_upgrading_notification_title" msgid="8428357096969413169">"Iqedela isibuyekezo se-Android…"</string>
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Ezinye izinhlelo zokusebenza kungenzeka zingasebenzi kahle kuze kuqedwe ukuthuthukiswa"</string>
+ <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> iyathuthukisa…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Ukubeka ezingeni eliphezulu <xliff:g id="NUMBER_0">%1$d</xliff:g> uhlelo lokusebenza <xliff:g id="NUMBER_1">%2$d</xliff:g>"</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"Ukulungisela i-<xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Qalisa izinhlelo zokusebenza."</string>
@@ -1651,7 +1653,16 @@
<string name="unpin_target" msgid="3556545602439143442">"Susa ukuphina"</string>
<string name="app_info" msgid="6856026610594615344">"Ulwazi lohlelo lokusebenza"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="reset_retail_demo_mode_title" msgid="2370249087943803584">"Setha kabusha idivayisi?"</string>
+ <string name="reset_retail_demo_mode_text" msgid="5481925817590883246">"Thepha ukuze usethe kabusha idivayisi"</string>
+ <string name="demo_starting_message" msgid="5268556852031489931">"Iqalisa i-demo..."</string>
+ <string name="demo_restarting_message" msgid="952118052531642451">"Isetha kabusha idivayisi..."</string>
+ <string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"Setha kabusha idivayisi?"</string>
+ <string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"Uzolahlekelwa inoma iluphi ushintsho futhi idemo izoqala futhi kumasekhondi angu-<xliff:g id="TIMEOUT">%1$s</xliff:g>..."</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"Khansela"</string>
+ <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Setha kabusha manje"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"Setha kabusha ukuze usebenzise idivayisi ngaphandle kwemikhawulo"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"Thinta ukuze ufunde kabanzi."</string>
<string name="suspended_widget_accessibility" msgid="6712143096475264190">"I-<xliff:g id="LABEL">%1$s</xliff:g> ekhutshaziwe"</string>
+ <string name="conference_call" msgid="3751093130790472426">"Ikholi yengqungquthela"</string>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 759b5a4..48e4201 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1048,6 +1048,9 @@
the status bar (via statusBarColor) and navigation bar (via navigationBarColor). -->
<attr name="colorPrimaryDark" format="color" />
+ <!-- The secondary branding color for the app. -->
+ <attr name="colorSecondary" format="color" />
+
<!-- Bright complement to the primary branding color. By default, this is the color applied
to framework controls (via colorControlActivated). -->
<attr name="colorAccent" format="color" />
@@ -1832,6 +1835,10 @@
<enum name="KEYCODE_CUT" value="277" />
<enum name="KEYCODE_COPY" value="278" />
<enum name="KEYCODE_PASTE" value="279" />
+ <enum name="KEYCODE_SYSTEM_NAVIGATION_UP" value="280" />
+ <enum name="KEYCODE_SYSTEM_NAVIGATION_DOWN" value="281" />
+ <enum name="KEYCODE_SYSTEM_NAVIGATION_LEFT" value="282" />
+ <enum name="KEYCODE_SYSTEM_NAVIGATION_RIGHT" value="283" />
</attr>
<!-- ***************************************************************** -->
@@ -2043,6 +2050,13 @@
<attr name="showTitle" format="boolean" />
<!-- @hide Whether fullDark, etc. should use default values if null. -->
<attr name="needsDefaultBackgrounds" format="boolean" />
+ <!-- @hide Workaround until we replace AlertController with custom layout. -->
+ <attr name="controllerType">
+ <!-- The default controller. -->
+ <enum name="normal" value="0" />
+ <!-- Controller for micro specific layout. -->
+ <enum name="micro" value="1" />
+ </attr>
</declare-styleable>
<!-- @hide -->
@@ -3777,7 +3791,10 @@
<!-- An optional argument to supply a maximum height for this view.
See {see android.widget.ImageView#setMaxHeight} for details. -->
<attr name="maxHeight" format="dimension" />
- <!-- Set a tinting color for the image. By default, the tint will blend using SRC_ATOP mode. -->
+ <!-- The tinting color for the image. By default, the tint will blend using SRC_ATOP mode.
+ Please note that for compatibility reasons, this is NOT consistent with the default
+ SRC_IN tint mode used by {@link android.widget.ImageView#setImageTintList} and by
+ similar tint attributes on other views. -->
<attr name="tint" format="color" />
<!-- If true, the image view will be baseline aligned with based on its
bottom edge. -->
@@ -7325,14 +7342,28 @@
<declare-styleable name="Wallpaper">
<attr name="settingsActivity" />
- <!-- Reference to a the wallpaper's thumbnail bitmap. -->
+ <!-- Reference to the wallpaper's thumbnail bitmap. -->
<attr name="thumbnail" format="reference" />
- <!-- Name of the author of this component, e.g. Google. -->
+ <!-- Name of the author and/or source/collection of this component, e.g. Art Collection, Picasso. -->
<attr name="author" format="reference" />
<!-- Short description of the component's purpose or behavior. -->
<attr name="description" />
+
+ <!-- Uri that specifies a link for further context of this wallpaper, e.g. http://www.picasso.org. -->
+ <attr name="contextUri" format="reference" />
+
+ <!-- Title of the uri that specifies a link for further context of this wallpaper, e.g. Explore collection. -->
+ <attr name="contextDescription" format="reference" />
+
+ <!-- Whether to show any metadata when previewing the wallpaper. If this value is
+ set to true, any component that shows a preview of this live wallpaper should also show
+ accompanying information like the title, the description, the author and the context
+ description of this wallpaper so the user gets to know further information about this
+ wallpaper. -->
+ <attr name="showMetadataInPreview" format="boolean" />
+
</declare-styleable>
<!-- Use <code>dream</code> as the root tag of the XML resource that
@@ -7871,6 +7902,13 @@
<attr name="divider" />
</declare-styleable>
+ <!-- Base attributes available to PreferenceScreen. -->
+ <declare-styleable name="PreferenceScreen">
+ <!-- The layout for the PreferenceScreen. This should rarely need to be changed. -->
+ <attr name="screenLayout" format="reference" />
+ <attr name="divider" />
+ </declare-styleable>
+
<!-- Base attributes available to PreferenceActivity. -->
<declare-styleable name="PreferenceActivity">
<!-- The layout for the Preference Activity. This should rarely need to be changed. -->
@@ -8227,4 +8265,17 @@
color. -->
<attr name="colorBackground" />
</declare-styleable>
+
+ <declare-styleable name="Shortcut">
+ <attr name="shortcutId" format="string" />
+ <attr name="enabled" />
+ <attr name="icon" />
+ <attr name="shortcutShortLabel" format="reference" />
+ <attr name="shortcutLongLabel" format="reference" />
+ <attr name="shortcutDisabledMessage" format="reference" />
+ </declare-styleable>
+
+ <declare-styleable name="ShortcutCategories">
+ <attr name="name" />
+ </declare-styleable>
</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index d69d73d..0872ef9 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -62,6 +62,20 @@
a reference to a Drawable resource containing the image definition. -->
<attr name="icon" format="reference" />
+ <!-- A Drawable resource providing a graphical representation of its
+ associated item. Use with the
+ application tag (to supply a default round icon for all application
+ components), or with the activity, receiver, service, or instrumentation
+ tag (to supply a specific round icon for that component). It may also be
+ used with the intent-filter tag to supply a round icon to show to the
+ user when an activity is being selected based on a particular Intent.
+
+ <p>The given round icon will be used to display to the user a graphical
+ representation of its associated component; for example, as the round icon
+ for main activity that is displayed in the launcher. This must be
+ a reference to a Drawable resource containing the image definition. -->
+ <attr name="roundIcon" format="reference" />
+
<!-- A Drawable resource providing an extended graphical banner for its
associated item. Use with the application tag (to supply a default
banner for all application activities), or with the activity, tag to
@@ -1236,6 +1250,7 @@
<attr name="theme" />
<attr name="label" />
<attr name="icon" />
+ <attr name="roundIcon" />
<attr name="banner" />
<attr name="logo" />
<attr name="description" />
@@ -1335,6 +1350,7 @@
<attr name="name" />
<attr name="label" />
<attr name="icon" />
+ <attr name="roundIcon" />
<attr name="banner" />
<attr name="logo" />
<attr name="permissionGroup" />
@@ -1362,6 +1378,7 @@
<attr name="name" />
<attr name="label" />
<attr name="icon" />
+ <attr name="roundIcon" />
<attr name="banner" />
<attr name="logo" />
<attr name="description" />
@@ -1395,6 +1412,7 @@
<attr name="name" />
<attr name="label" />
<attr name="icon" />
+ <attr name="roundIcon" />
<attr name="banner" />
<attr name="logo" />
</declare-styleable>
@@ -1676,6 +1694,7 @@
<attr name="label" />
<attr name="description" />
<attr name="icon" />
+ <attr name="roundIcon" />
<attr name="banner" />
<attr name="logo" />
<attr name="process" />
@@ -1759,6 +1778,7 @@
<attr name="label" />
<attr name="description" />
<attr name="icon" />
+ <attr name="roundIcon" />
<attr name="banner" />
<attr name="logo" />
<attr name="permission" />
@@ -1807,6 +1827,7 @@
<attr name="label" />
<attr name="description" />
<attr name="icon" />
+ <attr name="roundIcon" />
<attr name="banner" />
<attr name="logo" />
<attr name="permission" />
@@ -1843,6 +1864,7 @@
<attr name="label" />
<attr name="description" />
<attr name="icon" />
+ <attr name="roundIcon" />
<attr name="banner" />
<attr name="logo" />
<attr name="launchMode" />
@@ -1926,6 +1948,7 @@
<attr name="label" />
<attr name="description" />
<attr name="icon" />
+ <attr name="roundIcon" />
<attr name="banner" />
<attr name="logo" />
<attr name="permission" />
@@ -1998,6 +2021,7 @@
parent="AndroidManifestActivity AndroidManifestReceiver AndroidManifestService">
<attr name="label" />
<attr name="icon" />
+ <attr name="roundIcon" />
<attr name="banner" />
<attr name="logo" />
<attr name="priority" />
@@ -2128,6 +2152,7 @@
<attr name="targetPackage" />
<attr name="label" />
<attr name="icon" />
+ <attr name="roundIcon" />
<attr name="banner" />
<attr name="logo" />
<attr name="handleProfiling" />
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index bddd225..de86cef 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -74,6 +74,8 @@
<drawable name="input_method_fullscreen_background">#fff9f9f9</drawable>
<color name="input_method_navigation_guard">#ff000000</color>
+ <color name="system_error">#fff4511e</color> <!-- deep orange 600 -->
+
<!-- For date picker widget -->
<drawable name="selected_day_background">#ff0092f4</drawable>
@@ -118,7 +120,7 @@
<!-- LockPatternView -->
<color name="lock_pattern_view_regular_color">#ffffffff</color>
<color name="lock_pattern_view_success_color">#ffffffff</color>
- <color name="lock_pattern_view_error_color">#fff4511e</color>
+ <color name="lock_pattern_view_error_color">@color/system_error</color>
<!-- FaceLock -->
<color name="facelock_spotlight_mask">#CC000000</color>
@@ -138,6 +140,7 @@
<color name="notification_progress_background_color">@color/secondary_text_material_light</color>
<color name="notification_action_list">#ffeeeeee</color>
+ <color name="notification_action_list_dark">#ffe0e0e0</color>
<!-- Keyguard colors -->
<color name="keyguard_avatar_frame_color">#ffffffff</color>
@@ -151,7 +154,7 @@
<color name="battery_saver_mode_color">#fff4511e</color><!-- deep orange 600 -->
<!-- Default user icon colors -->
- <color name="user_icon_1">#ff00bcd4</color><!-- teal 500 -->
+ <color name="user_icon_1">#ff00bcd4</color><!-- cyan 500 -->
<color name="user_icon_2">#ff3f51b5</color><!-- indigo 500 -->
<color name="user_icon_3">#ff4285f4</color><!-- blue 500 -->
<color name="user_icon_4">#ffe91e63</color><!-- pink 500 -->
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
new file mode 100644
index 0000000..27ee27b
--- /dev/null
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Colors specific to DeviceDefault themes. These are mostly pass-throughs to enable
+ overlaying new theme colors. -->
+<resources>
+ <color name="primary_device_default_dark">@color/primary_material_dark</color>
+ <color name="primary_device_default_light">@color/primary_material_light</color>
+ <color name="primary_device_default_settings">@color/primary_material_settings</color>
+ <color name="primary_dark_device_default_dark">@color/primary_dark_material_dark</color>
+ <color name="primary_dark_device_default_light">@color/primary_dark_material_light</color>
+ <color name="primary_dark_device_default_settings">@color/primary_dark_material_settings</color>
+
+ <color name="secondary_device_default_settings">@color/secondary_material_settings</color>
+ <color name="tertiary_device_default_settings">@color/tertiary_material_settings</color>
+ <color name="quaternary_device_default_settings">@color/quaternary_material_settings</color>
+
+ <color name="accent_device_default_700">@color/material_deep_teal_700</color>
+ <color name="accent_device_default_light">@color/accent_material_light</color>
+ <color name="accent_device_default_dark">@color/accent_material_dark</color>
+ <color name="accent_device_default_50">@color/material_deep_teal_50</color>
+</resources>
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index c8ca116..a864cf3 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -26,9 +26,15 @@
<color name="primary_material_dark">@color/material_grey_900</color>
<color name="primary_material_light">@color/material_grey_100</color>
+ <color name="primary_material_settings">@color/material_blue_grey_900</color>
<color name="primary_dark_material_dark">@color/black</color>
<color name="primary_dark_material_light">@color/material_grey_600</color>
<color name="primary_dark_material_light_light_status_bar">@color/material_grey_300</color>
+ <color name="primary_dark_material_settings">@color/material_blue_grey_950</color>
+
+ <color name="secondary_material_settings">@color/material_blue_grey_800</color>
+ <color name="tertiary_material_settings">@color/material_blue_grey_700</color>
+ <color name="quaternary_material_settings">@color/material_blue_grey_200</color>
<color name="accent_material_light">@color/material_deep_teal_500</color>
<color name="accent_material_dark">@color/material_deep_teal_200</color>
@@ -75,11 +81,15 @@
<color name="material_grey_100">#fff5f5f5</color>
<color name="material_grey_50">#fffafafa</color>
+ <color name="material_deep_teal_50">#ffe0f2f1</color>
<color name="material_deep_teal_100">#ffb2dfdb</color>
<color name="material_deep_teal_200">#ff80cbc4</color>
<color name="material_deep_teal_300">#ff4db6ac</color>
<color name="material_deep_teal_500">#ff009688</color>
+ <color name="material_deep_teal_700">#ff00796b</color>
+ <color name="material_blue_grey_200">#ffb0bec5</color>
+ <color name="material_blue_grey_700">#ff455a64</color>
<color name="material_blue_grey_800">#ff37474f</color>
<color name="material_blue_grey_900">#ff263238</color>
<color name="material_blue_grey_950">#ff21272b</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d0fd36a..b80b0a7 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -268,6 +268,13 @@
<!-- The maximum duration (in milliseconds) we expect a network transition to take -->
<integer name="config_networkTransitionTimeout">60000</integer>
+ <!-- Whether/how to notify the user on network switches. See LingerMonitor.java. -->
+ <integer translatable="false" name="config_networkNotifySwitchType">0</integer>
+
+ <!-- What types of network switches to notify. See LingerMonitor.java. -->
+ <string-array translatable="false" name="config_networkNotifySwitches">
+ </string-array>
+
<!-- List of regexpressions describing the interface (if any) that represent tetherable
USB interfaces. If the device doesn't want to support tethering over USB this should
be empty. An example would be "usb.*" -->
@@ -427,8 +434,11 @@
<!-- Boolean indicating whether or not wifi firmware debugging is enabled -->
<bool translatable="false" name="config_wifi_enable_wifi_firmware_debugging">true</bool>
- <!-- Integer size limit, in KB, for a single WifiLogger ringbuffer -->
- <integer translatable="false" name="config_wifi_logger_ring_buffer_size_limit_kb">32</integer>
+ <!-- Integer size limit, in KB, for a single WifiLogger ringbuffer, in default logging mode -->
+ <integer translatable="false" name="config_wifi_logger_ring_buffer_default_size_limit_kb">32</integer>
+
+ <!-- Integer size limit, in KB, for a single WifiLogger ringbuffer, in verbose logging mode -->
+ <integer translatable="false" name="config_wifi_logger_ring_buffer_verbose_size_limit_kb">1024</integer>
<!-- Boolean indicating whether or not wifi should turn off when emergency call is made -->
<bool translatable="false" name="config_wifi_turn_off_during_emergency_call">false</bool>
@@ -751,6 +761,26 @@
-->
<integer name="config_defaultNightMode">1</integer>
+ <!-- Control whether Night display is available. This should only be enabled on devices
+ with HWC 2.0 or higher. -->
+ <bool name="config_nightDisplayAvailable">false</bool>
+
+ <!-- Default mode to control how Night display is automatically activated.
+ One of the following values (see NightDisplayController.java):
+ 0 - AUTO_MODE_DISABLED
+ 1 - AUTO_MODE_CUSTOM
+ 2 - AUTO_MODE_TWILIGHT
+ -->
+ <integer name="config_defaultNightDisplayAutoMode">0</integer>
+
+ <!-- Default time when Night display is automatically activated.
+ Represented as milliseconds from midnight (e.g. 79200000 == 10pm). -->
+ <integer name="config_defaultNightDisplayCustomStartTime">79200000</integer>
+
+ <!-- Default time when Night display is automatically deactivated.
+ Represented as milliseconds from midnight (e.g. 21600000 == 6am). -->
+ <integer name="config_defaultNightDisplayCustomEndTime">21600000</integer>
+
<!-- Indicate whether to allow the device to suspend when the screen is off
due to the proximity sensor. This resource should only be set to true
if the sensor HAL correctly handles the proximity sensor as a wake-up source.
@@ -1802,28 +1832,6 @@
-->
<bool name="config_enableWifiDisplay">false</bool>
- <!-- The color transform values that correspond to each respective configuration mode for the
- built-in display, or -1 if the mode is unsupported by the device. The possible
- configuration modes are:
- 1. Wide-gamut ("Vibrant")
- 2. Adobe RGB ("Natural")
- 3. sRGB ("Standard")
-
- For example, if a device had Wide-gamut as color transform mode 1, sRGB mode as color
- transform mode 7, and did not support Adobe RGB at all this would look like:
-
- <integer-array name="config_colorTransforms">
- <item>1</item>
- <item>-1</item>
- <item>7</item>
- </integer-array>
- -->
- <integer-array name="config_colorTransforms">
- <item>-1</item>
- <item>-1</item>
- <item>-1</item>
- </integer-array>
-
<!-- When true, local displays that do not contain any of their own content will automatically
mirror the content of the default display. -->
<bool name="config_localDisplaysMirrorContent">true</bool>
@@ -2070,9 +2078,11 @@
"bugreport" = Take bug report, if available
"silent" = silent mode
"users" = list of users
+ "restart" = restart device
-->
<string-array translatable="false" name="config_globalActionsList">
<item>power</item>
+ <item>restart</item>
<item>bugreport</item>
<item>users</item>
</string-array>
@@ -2461,6 +2471,10 @@
<!-- True if the device supports Sustained Performance Mode-->
<bool name="config_sustainedPerformanceModeSupported">false</bool>
+ <!-- File used to enable the double touch gesture.
+ TODO: move to input HAL once ready. -->
+ <string name="config_doubleTouchGestureEnableFile"></string>
+
<!-- Controls how we deal with externally connected physical keyboards.
0 - When using this device, it is not clear for users to recognize when the physical
keyboard is (should be) connected and when it is (should be) disconnected. Most of
@@ -2486,4 +2500,39 @@
<string-array translatable="false" name="config_defaultPinnerServiceFiles">
</string-array>
+ <!-- True if camera app should be pinned via Pinner Service -->
+ <bool name="config_pinnerCameraApp">false</bool>
+
+ <!-- Component that is the default launcher when demo mode is enabled. -->
+ <string name="config_demoModeLauncherComponent">com.android.retaildemo/.DemoPlayer</string>
+
+ <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
+ <bool name="config_useRoundIcon">false</bool>
+
+ <!-- Flag indicating whether the assist disclosure can be disabled using
+ ASSIST_DISCLOSURE_ENABLED. -->
+ <bool name="config_allowDisablingAssistDisclosure">false</bool>
+
+ <!-- True if the device supports system navigation keys. -->
+ <bool name="config_supportSystemNavigationKeys">false</bool>
+
+ <!-- Package name for the device provisioning package. -->
+ <string name="config_deviceProvisioningPackage"></string>
+
+ <!-- Colon separated list of package names that should be granted DND access -->
+ <string name="config_defaultDndAccessPackages" translatable="false">com.android.camera2</string>
+
+ <!-- User restrictions set when the first user is created.
+ Note: Also update appropriate overlay files. -->
+ <string-array translatable="false" name="config_defaultFirstUserRestrictions">
+ </string-array>
+
+ <!-- A array of regex to treat a SMS as VVM SMS if the message body matches.
+ Each item represents an entry, which consists of two parts:
+ a comma (,) separated list of MCCMNC the regex applies to, followed by a semicolon (;), and
+ then the regex itself. -->
+ <string-array translatable="false" name="config_vvmSmsFilterRegexes">
+ <!-- Verizon requires any SMS that starts with //VZWVVM to be treated as a VVM SMS-->
+ <item>310004,310010,310012,310013,310590,310890,310910,311110,311270,311271,311272,311273,311274,311275,311276,311277,311278,311279,311280,311281,311282,311283,311284,311285,311286,311287,311288,311289,311390,311480,311481,311482,311483,311484,311485,311486,311487,311488,311489;^//VZWVVM.*</item>
+ </string-array>
</resources>
diff --git a/core/res/res/values/config_material.xml b/core/res/res/values/config_material.xml
new file mode 100644
index 0000000..a37be83
--- /dev/null
+++ b/core/res/res/values/config_material.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds, only for Material theme. Do not translate.
+
+ NOTE: The naming convention is "config_camelCaseValue". -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- True if the device supports action bars. -->
+ <bool name="config_windowActionBarSupported">true</bool>
+
+ <!-- True if the device should have titles by default. -->
+ <bool name="config_windowNoTitleDefault">false</bool>
+
+ <!-- The alert controller to use for alert dialogs. -->
+ <integer name="config_alertDialogController">0</integer>
+
+ <!-- True if windowOverscan should be on by default. -->
+ <bool name="config_windowOverscanByDefault">false</bool>
+
+ <!-- Max number of lines for the dialog title. -->
+ <integer name="config_dialogWindowTitleMaxLines">1</integer>
+
+ <!-- True if preference fragment should clip to padding. -->
+ <bool name="config_preferenceFragmentClipToPadding">true</bool>
+</resources>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 00e48a0..f96cef9 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -24,6 +24,8 @@
<!-- Preference fragment padding, sides -->
<dimen name="preference_fragment_padding_side_material">0dp</dimen>
+ <!-- Preference fragment padding, vertical -->
+ <dimen name="preference_fragment_padding_vertical_material">0dp</dimen>
<!-- Preference breadcrumbs padding, start padding -->
<dimen name="preference_breadcrumbs_padding_start_material">12dp</dimen>
@@ -53,6 +55,8 @@
<!-- Default padding for list items. This should match the action bar
content inset so that ListActivity items line up correctly. -->
<dimen name="list_item_padding_horizontal_material">@dimen/action_bar_content_inset_material</dimen>
+ <dimen name="list_item_padding_start_material">@dimen/action_bar_content_inset_material</dimen>
+ <dimen name="list_item_padding_end_material">@dimen/action_bar_content_inset_material</dimen>
<!-- Padding to add to the start of the overflow action button. -->
<dimen name="action_bar_overflow_padding_start_material">6dp</dimen>
@@ -84,6 +88,8 @@
<dimen name="text_size_medium_material">18sp</dimen>
<dimen name="text_size_small_material">14sp</dimen>
+ <item name="text_line_spacing_multiplier_material" format="float" type="dimen">1.0</item>
+
<dimen name="text_edit_floating_toolbar_elevation">2dp</dimen>
<dimen name="text_edit_floating_toolbar_margin">20dp</dimen>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 7f8acd3..5c165e6 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -122,9 +122,11 @@
<!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SET_PROGRESS}. -->
<item type="id" name="accessibilityActionSetProgress" />
-
+
<!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_CONTEXT_CLICK}. -->
<item type="id" name="accessibilityActionContextClick" />
<item type="id" name="remote_input_tag" />
+
+ <item type="id" name="cross_task_transition" />
</resources>
diff --git a/core/res/res/values/integers.xml b/core/res/res/values/integers.xml
index 8f8d59e..71ac2f4 100644
--- a/core/res/res/values/integers.xml
+++ b/core/res/res/values/integers.xml
@@ -26,4 +26,5 @@
<integer name="date_picker_mode">1</integer>
<integer name="time_picker_mode">1</integer>
+ <integer name="date_picker_header_max_lines_material">2</integer>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 6797799..77de87d 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2728,4 +2728,18 @@
<public type="id" name="icon_frame" id="0x0102003e" />
<public type="id" name="list_container" id="0x0102003f" />
<public type="id" name="switch_widget" id="0x01020040" />
+ <!-- ===============================================================
+ Resources added in version N MR1 of the platform
+ =============================================================== -->
+ <eat-comment />
+ <public type="attr" name="shortcutId" id="0x01010528" />
+ <public type="attr" name="shortcutShortLabel" id="0x01010529" />
+ <public type="attr" name="shortcutLongLabel" id="0x0101052a" />
+ <public type="attr" name="shortcutDisabledMessage" id="0x0101052b" />
+ <public type="attr" name="roundIcon" id="0x0101052c" />
+ <public type="attr" name="contextUri" id="0x0101052d" />
+ <public type="attr" name="contextDescription" id="0x0101052e" />
+ <public type="attr" name="showMetadataInPreview" id="0x0101052f" />
+ <public type="attr" name="colorSecondary" id="0x01010530" />
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index b55a9b22..9002ed4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -482,6 +482,10 @@
<!-- label for item that turns off power in phone options dialog -->
<string name="global_action_power_off">Power off</string>
+ <!-- label for item that restarts phone in phone options dialog -->
+ <!-- TODO: promote to separate string-->
+ <string name="global_action_restart" translatable="false">@string/sim_restart_button</string>
+
<!-- label for item that generates a bug report in the phone options dialog -->
<string name="global_action_bug_report">Bug report</string>
@@ -1756,6 +1760,10 @@
<string name="lockscreen_pattern_wrong">Try again</string>
<!-- On the unlock password screen, shown when the user enters the wrong lock password and must try again. -->
<string name="lockscreen_password_wrong">Try again</string>
+
+ <!-- On the keyguard screen, this string explains that some features or data may not be available until the device is unlocked. [CHAR LIMIT=48] -->
+ <string name="lockscreen_storage_locked">Unlock for all features and data</string>
+
<!-- Shown when face unlock failed multiple times so we're just using the backup -->
<string name="faceunlock_multiple_failures">Maximum Face Unlock attempts exceeded</string>
@@ -2789,11 +2797,14 @@
<!-- [CHAR LIMIT=NONE] Message shown in upgrading dialog when doing an fstrim. -->
<string name="android_upgrading_fstrim">Optimizing storage.</string>
- <!-- [CHAR LIMIT=40] Title of notification that is shown when performing a system upgrade. -->
- <string name="android_upgrading_notification_title">Android is upgrading</string>
+ <!-- [CHAR LIMIT=40] Title of notification that is shown when finishing a system upgrade. -->
+ <string name="android_upgrading_notification_title">Finishing Android update\u2026</string>
<!-- [CHAR LIMIT=200] Body of notification that is shown when performing a system upgrade. -->
<string name="android_upgrading_notification_body">Some apps may not work properly until the upgrade finishes</string>
+ <!-- [CHAR LIMIT=40] Toast that is shown when an app is still upgrading. -->
+ <string name="app_upgrading_toast"><xliff:g id="application">%1$s</xliff:g> is upgrading\u2026</string>
+
<!-- [CHAR LIMIT=NONE] Message shown in upgrading dialog for each .apk that is optimized. -->
<string name="android_upgrading_apk">Optimizing app
<xliff:g id="number" example="123">%1$d</xliff:g> of
@@ -2918,6 +2929,27 @@
<!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's message. -->
<string name="wifi_no_internet_detailed">Tap for options</string>
+ <!-- A notification might be shown if the device switches to another network type (e.g., cellular data) because it detects that the network it was using (e.g., Wi-Fi) has lost Internet connectivity. This is the notification's title. %1$s is the network type that the device switched to, e.g., cellular data. It is one of the strings in the network_switch_type_name array. -->
+ <string name="network_switch_metered">Switched to <xliff:g id="network_type">%1$s</xliff:g></string>
+
+ <!-- A notification might be shown if the device switches to another network type (e.g., cellular data) because it detects that the network it was using (e.g., Wi-Fi) has lost Internet connectivity. This is the notification's message. %1$s is the network that the device switched to, e.g., cellular data. %2$s is the network type the device switched from, e.g., Wi-Fi. Both are strings in the network_switch_type_name array. -->
+ <string name="network_switch_metered_detail">Device uses <xliff:g id="new_network">%1$s</xliff:g> when <xliff:g id="previous_network">%2$s</xliff:g> has no Internet access. Charges may apply.</string>
+
+ <!-- A toast might be shown if the device switches to another network type (e.g., cellular data) because it detects that the network it was using (e.g., Wi-Fi) has lost Internet connectivity. This is the text of the toast. %1$s is the network that the device switched from, e.g., Wi-Fi. %2$s is the network type the device switched from, e.g., cellular data. Both are strings in the network_switch_type_name array. -->
+ <string name="network_switch_metered_toast">Switched from <xliff:g id="previous_network">%1$s</xliff:g> to <xliff:g id="new_network">%2$s</xliff:g></string>
+
+ <!-- Network type names used in the network_switch_metered and network_switch_metered_detail strings. These must be kept in the sync with the values NetworkCapabilities.TRANSPORT_xxx values, and in the same order. -->
+ <string-array name="network_switch_type_name">
+ <item>cellular data</item>
+ <item>Wi-Fi</item>
+ <item>Bluetooth</item>
+ <item>Ethernet</item>
+ <item>VPN</item>
+ </string-array>
+
+ <!-- Network type name displayed if one of the types is not found in network_switch_type_name. -->
+ <string name="network_switch_type_name_unknown">an unknown network type</string>
+
<!-- A notification is shown when a user's selected SSID is later disabled due to connectivity problems. This is the notification's title / ticker. -->
<string name="wifi_watchdog_network_disabled">Couldn\'t connect to Wi-Fi</string>
<!-- A notification is shown when a user's selected SSID is later disabled due to connectivity problems. The complete alert msg is: <hotspot name> + this string, i.e. "Linksys has a poor internet connection" -->
@@ -4158,7 +4190,7 @@
<!-- [CHAR_LIMIT=NONE] Data saver: Feature description -->
<string name="data_saver_description">To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them.</string>
- <!-- [CHAR_LIMIT=30] Data saver: Title on first-time dialogFeature description -->
+ <!-- [CHAR_LIMIT=35] Data saver: Title on first-time dialog -->
<string name="data_saver_enable_title">Turn on Data Saver?</string>
<!-- [CHAR_LIMIT=16] Data saver: Button to turn it on on first-time dialog -->
<string name="data_saver_enable_button">Turn on</string>
@@ -4371,6 +4403,23 @@
<!-- The representation of a time duration when negative. An example is -1:14. This can be used with a countdown timer for example.-->
<string name="negative_duration">\u2212<xliff:g id="time" example="1:14">%1$s</xliff:g></string>
+ <!-- Title of notification to start a new demo session when device is in retail mode [CHAR LIMIT=NONE] -->
+ <string name="reset_retail_demo_mode_title">Reset device?</string>
+ <!-- Text of notification to start a new demo session when device is in retail mode [CHAR LIMIT=NONE] -->
+ <string name="reset_retail_demo_mode_text">Tap to reset device</string>
+ <!-- Text of dialog shown when starting a demo user for the first time [CHAR LIMIT=40] -->
+ <string name="demo_starting_message">Starting demo\u2026</string>
+ <!-- Text of dialog shown when starting a new demo user in retail demo mode [CHAR LIMIT=40] -->
+ <string name="demo_restarting_message">Resetting device\u2026</string>
+ <!-- Title of the dialog shown when user inactivity times out in retail demo mode [CHAR LIMIT=40] -->
+ <string name="demo_user_inactivity_timeout_title">Reset device?</string>
+ <!-- Warning message shown when user inactivity times out in retail demo mode [CHAR LIMIT=none] -->
+ <string name="demo_user_inactivity_timeout_countdown">You\u2019ll lose any changes and the demo will start again in <xliff:g id="timeout" example="9">%1$s</xliff:g> seconds\u2026</string>
+ <!-- Text of button to allow user to abort countdown and continue current session in retail demo mode [CHAR LIMIT=40] -->
+ <string name="demo_user_inactivity_timeout_left_button">Cancel</string>
+ <!-- Text of button to allow user to abort countdown and immediately start another session in retail demo mode [CHAR LIMIT=40] -->
+ <string name="demo_user_inactivity_timeout_right_button">Reset now</string>
+
<!-- Title of notification shown when device has been forced to safe mode after a security compromise. -->
<string name="audit_safemode_notification">Factory reset to use this device without restrictions</string>
<!-- Description of notification shown when device has been forced to safe mode after a security compromise. -->
@@ -4379,4 +4428,6 @@
<!-- Accessibilty string added to a widget that has been suspended [CHAR LIMIT=20] -->
<string name="suspended_widget_accessibility">Disabled <xliff:g id="label" example="Calendar">%1$s</xliff:g></string>
+ <!-- Label used by Telephony code, assigned as the display name for conference calls [CHAR LIMIT=60] -->
+ <string name="conference_call">Conference Call</string>
</resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 790dcfa..762cf31 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1122,6 +1122,8 @@
<style name="PreferenceFragmentList">
<item name="paddingStart">@dimen/preference_fragment_padding_side</item>
<item name="paddingEnd">@dimen/preference_fragment_padding_side</item>
+ <item name="paddingTop">0dp</item>
+ <item name="paddingBottom">@dimen/preference_fragment_padding_bottom</item>
</style>
<!-- Other Misc Styles -->
@@ -1408,7 +1410,7 @@
<item name="paddingEnd">?attr/dialogPreferredPadding</item>
<item name="background">?attr/selectableItemBackground</item>
<item name="drawablePadding">32dp</item>
- <item name="drawableTint">@color/accent_material_light</item>
+ <item name="drawableTint">?android:attr/colorAccent</item>
<item name="drawableTintMode">src_atop</item>
</style>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index bb07834..4435537 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -36,13 +36,18 @@
<item name="layout">@layout/preference_material</item>
</style>
- <style name="PreferenceFragment.Material">
+ <style name="BasePreferenceFragment">
<item name="layout">@layout/preference_list_fragment_material</item>
<item name="paddingStart">@dimen/preference_fragment_padding_side_material</item>
<item name="paddingEnd">@dimen/preference_fragment_padding_side_material</item>
+ <item name="paddingTop">@dimen/preference_fragment_padding_vertical_material</item>
+ <item name="paddingBottom">@dimen/preference_fragment_padding_vertical_material</item>
<item name="divider">?attr/listDivider</item>
+ <item name="clipToPadding">@bool/config_preferenceFragmentClipToPadding</item>
</style>
+ <style name="PreferenceFragment.Material" parent="BasePreferenceFragment"/>
+
<style name="PreferenceActivity.Material">
<item name="layout">@layout/preference_list_content_material</item>
<item name="headerLayout">@layout/preference_header_item_material</item>
@@ -76,7 +81,12 @@
<item name="layout">@layout/preference_widget_seekbar_material</item>
</style>
- <style name="Preference.Material.PreferenceScreen"/>
+ <style name="Preference.Material.BasePreferenceScreen">
+ <item name="screenLayout">@layout/preference_list_fragment_material</item>
+ <item name="divider">?attr/listDivider</item>
+ </style>
+
+ <style name="Preference.Material.PreferenceScreen" parent="Preference.Material.BasePreferenceScreen"/>
<style name="Preference.Material.DialogPreference">
<item name="positiveButtonText">@string/ok</item>
@@ -134,6 +144,8 @@
<style name="PreferenceFragmentList.Material">
<item name="paddingStart">@dimen/preference_fragment_padding_side_material</item>
<item name="paddingEnd">@dimen/preference_fragment_padding_side_material</item>
+ <item name="paddingTop">@dimen/preference_fragment_padding_vertical_material</item>
+ <item name="paddingBottom">@dimen/preference_fragment_padding_vertical_material</item>
</style>
<!-- Begin Material theme styles -->
@@ -147,6 +159,7 @@
<item name="textColorLink">?attr/textColorLink</item>
<item name="textSize">@dimen/text_size_body_1_material</item>
<item name="fontFamily">@string/font_family_body_1_material</item>
+ <item name="lineSpacingMultiplier">@dimen/text_line_spacing_multiplier_material</item>
</style>
<style name="TextAppearance.Material.Display4">
@@ -535,7 +548,11 @@
<item name="textOff">@string/capital_off</item>
</style>
- <style name="Widget.Material.ButtonBar">
+ <style name="Widget.Material.BaseButtonBar">
+ <item name="background">?attr/colorBackgroundFloating</item>
+ </style>
+
+ <style name="Widget.Material.ButtonBar" parent="Widget.Material.BaseButtonBar">
<item name="background">@null</item>
</style>
@@ -1190,12 +1207,17 @@
<item name="listItemLayout">@layout/select_dialog_item_material</item>
<item name="multiChoiceItemLayout">@layout/select_dialog_multichoice_material</item>
<item name="singleChoiceItemLayout">@layout/select_dialog_singlechoice_material</item>
+ <item name="controllerType">@integer/config_alertDialogController</item>
</style>
<style name="AlertDialog.Material.Light" />
<style name="DatePickerDialog.Material" parent="AlertDialog.Material">
- <item name="showTitle">true</item>
+ <item name="showTitle">false</item>
+ </style>
+
+ <style name="TimePickerDialog.Material" parent="AlertDialog.Material">
+ <item name="showTitle">false</item>
</style>
<!-- Window title -->
@@ -1224,7 +1246,7 @@
<style name="DialogWindowTitleBackground.Material.Light" />
<style name="DialogWindowTitle.Material">
- <item name="maxLines">1</item>
+ <item name="maxLines">@integer/config_dialogWindowTitleMaxLines</item>
<item name="scrollHorizontally">true</item>
<item name="textAppearance">@style/TextAppearance.Material.DialogWindowTitle</item>
</style>
diff --git a/core/res/res/values/styles_micro.xml b/core/res/res/values/styles_micro.xml
deleted file mode 100644
index 341a0a4..0000000
--- a/core/res/res/values/styles_micro.xml
+++ /dev/null
@@ -1,93 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2014 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.
--->
-<resources>
- <style name="Animation.Micro"/>
-
- <style name="Animation.Micro.Activity" parent="Animation.Material.Activity">
- <item name="activityOpenEnterAnimation">@anim/slide_in_enter_micro</item>
- <item name="activityOpenRemoteViewsEnterAnimation">@anim/slide_in_enter_micro</item>
- <item name="activityOpenExitAnimation">@anim/slide_in_exit_micro</item>
- <item name="activityCloseEnterAnimation">@null</item>
- <item name="activityCloseExitAnimation">@anim/slide_out_micro</item>
- <item name="taskOpenEnterAnimation">@anim/slide_in_enter_micro</item>
- <item name="taskOpenExitAnimation">@anim/slide_in_exit_micro</item>
- <item name="taskCloseEnterAnimation">@null</item>
- <item name="taskCloseExitAnimation">@anim/slide_out_micro</item>
- <item name="taskToFrontEnterAnimation">@anim/slide_in_enter_micro</item>
- <item name="taskToFrontExitAnimation">@anim/slide_in_exit_micro</item>
- <item name="taskToBackEnterAnimation">@null</item>
- <item name="taskToBackExitAnimation">@anim/slide_out_micro</item>
- <item name="wallpaperOpenEnterAnimation">@null</item>
- <item name="wallpaperOpenExitAnimation">@anim/slide_out_micro</item>
- <item name="wallpaperCloseEnterAnimation">@anim/slide_in_enter_micro</item>
- <item name="wallpaperCloseExitAnimation">@anim/slide_in_exit_micro</item>
- <item name="wallpaperIntraOpenEnterAnimation">@null</item>
- <item name="wallpaperIntraOpenExitAnimation">@anim/slide_out_micro</item>
- <item name="wallpaperIntraCloseEnterAnimation">@anim/slide_in_enter_micro</item>
- <item name="wallpaperIntraCloseExitAnimation">@anim/slide_in_exit_micro</item>
- </style>
-
- <style name="AlertDialog.Micro" parent="AlertDialog.Material.Light">
- <item name="fullDark">@null</item>
- <item name="topDark">@null</item>
- <item name="centerDark">@null</item>
- <item name="bottomDark">@null</item>
- <item name="fullBright">@null</item>
- <item name="topBright">@null</item>
- <item name="centerBright">@null</item>
- <item name="bottomBright">@null</item>
- <item name="bottomMedium">@null</item>
- <item name="centerMedium">@null</item>
- <item name="layout">@layout/alert_dialog_micro</item>
- </style>
-
- <style name="DialogWindowTitle.Micro">
- <item name="maxLines">1</item>
- <item name="scrollHorizontally">true</item>
- <item name="textAppearance">@style/TextAppearance.Micro.DialogWindowTitle</item>
- </style>
-
- <style name="TextAppearance.Micro" parent="TextAppearance.Material">
- <item name="textSize">20sp</item>
- <item name="fontFamily">sans-serif-condensed-light</item>
- <item name="textColor">@color/micro_text_light</item>
- </style>
-
- <style name="TextAppearance.Micro.DialogWindowTitle" parent="TextAppearance.Material.DialogWindowTitle">
- <item name="textSize">20sp</item>
- <item name="fontFamily">sans-serif-condensed-light</item>
- <item name="textColor">@color/micro_text_light</item>
- </style>
-
- <style name="Widget.Micro" parent="Widget.Material" />
-
- <style name="Widget.Micro.TextView">
- <item name="fontFamily">sans-serif-condensed</item>
- </style>
-
- <style name="Widget.Micro.NumberPicker">
- <item name="internalLayout">@layout/number_picker_with_selector_wheel_micro</item>
- <item name="solidColor">@color/transparent</item>
- <item name="selectionDivider">@drawable/numberpicker_selection_divider</item>
- <item name="selectionDividerHeight">0dip</item>
- <item name="selectionDividersDistance">104dip</item>
- <item name="internalMinWidth">64dip</item>
- <item name="internalMaxHeight">180dip</item>
- <item name="virtualButtonPressedDrawable">?attr/selectableItemBackground</item>
- <item name="descendantFocusability">blocksDescendants</item>
- </style>
-
-</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d154b03..924d338 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -297,7 +297,8 @@
<java-symbol type="bool" name="config_wifi_enable_disconnection_debounce" />
<java-symbol type="bool" name="config_wifi_revert_country_code_on_cellular_loss" />
<java-symbol type="bool" name="config_wifi_enable_wifi_firmware_debugging" />
- <java-symbol type="integer" name="config_wifi_logger_ring_buffer_size_limit_kb" />
+ <java-symbol type="integer" name="config_wifi_logger_ring_buffer_default_size_limit_kb" />
+ <java-symbol type="integer" name="config_wifi_logger_ring_buffer_verbose_size_limit_kb" />
<java-symbol type="bool" name="config_wifi_turn_off_during_emergency_call" />
<java-symbol type="bool" name="config_supportMicNearUltrasound" />
<java-symbol type="bool" name="config_supportSpeakerNearUltrasound" />
@@ -946,6 +947,11 @@
<java-symbol type="string" name="wifi_available_sign_in" />
<java-symbol type="string" name="network_available_sign_in" />
<java-symbol type="string" name="network_available_sign_in_detailed" />
+ <java-symbol type="string" name="network_switch_metered" />
+ <java-symbol type="string" name="network_switch_metered_detail" />
+ <java-symbol type="string" name="network_switch_metered_toast" />
+ <java-symbol type="array" name="network_switch_type_name" />
+ <java-symbol type="string" name="network_switch_type_name_unknown" />
<java-symbol type="string" name="wifi_no_internet" />
<java-symbol type="string" name="wifi_no_internet_detailed" />
<java-symbol type="string" name="wifi_connect_alert_title" />
@@ -1103,6 +1109,11 @@
<java-symbol type="string" name="lockscreen_transport_pause_description" />
<java-symbol type="string" name="config_ethernet_tcp_buffers" />
<java-symbol type="string" name="config_wifi_tcp_buffers" />
+ <java-symbol type="string" name="config_demoModeLauncherComponent" />
+ <java-symbol type="string" name="demo_starting_message" />
+ <java-symbol type="string" name="demo_restarting_message" />
+ <java-symbol type="string" name="conference_call" />
+
<java-symbol type="plurals" name="bugreport_countdown" />
<java-symbol type="plurals" name="duration_hours" />
@@ -1136,7 +1147,6 @@
<java-symbol type="array" name="config_telephonyHardware" />
<java-symbol type="array" name="config_keySystemUuidMapping" />
<java-symbol type="array" name="config_gpsParameters" />
- <java-symbol type="array" name="config_colorTransforms" />
<java-symbol type="drawable" name="default_wallpaper" />
<java-symbol type="drawable" name="default_lock_wallpaper" />
@@ -1584,6 +1594,7 @@
<java-symbol type="string" name="bugreport_title" />
<java-symbol type="string" name="faceunlock_multiple_failures" />
<java-symbol type="string" name="global_action_power_off" />
+ <java-symbol type="string" name="global_action_restart" />
<java-symbol type="string" name="global_actions_airplane_mode_off_status" />
<java-symbol type="string" name="global_actions_airplane_mode_on_status" />
<java-symbol type="string" name="global_actions_toggle_airplane_mode" />
@@ -1733,6 +1744,8 @@
<java-symbol type="integer" name="config_lowBatteryWarningLevel" />
<java-symbol type="integer" name="config_networkPolicyDefaultWarning" />
<java-symbol type="integer" name="config_networkTransitionTimeout" />
+ <java-symbol type="integer" name="config_networkNotifySwitchType" />
+ <java-symbol type="array" name="config_networkNotifySwitches" />
<java-symbol type="integer" name="config_notificationsBatteryFullARGB" />
<java-symbol type="integer" name="config_notificationsBatteryLedOff" />
<java-symbol type="integer" name="config_notificationsBatteryLedOn" />
@@ -1859,7 +1872,6 @@
<java-symbol type="string" name="vpn_lockdown_config" />
<java-symbol type="string" name="wallpaper_binding_label" />
<java-symbol type="style" name="Theme.Dialog.AppError" />
- <java-symbol type="style" name="Theme.Micro.Dialog.Alert" />
<java-symbol type="style" name="Theme.Leanback.Dialog.Alert" />
<java-symbol type="style" name="Theme.Toast" />
<java-symbol type="xml" name="storage_list" />
@@ -1887,6 +1899,12 @@
<java-symbol type="string" name="config_persistentDataPackageName" />
<java-symbol type="string" name="audit_safemode_notification" />
<java-symbol type="string" name="audit_safemode_notification_details" />
+ <java-symbol type="string" name="reset_retail_demo_mode_title" />
+ <java-symbol type="string" name="reset_retail_demo_mode_text" />
+ <java-symbol type="string" name="demo_user_inactivity_timeout_title" />
+ <java-symbol type="string" name="demo_user_inactivity_timeout_countdown" />
+ <java-symbol type="string" name="demo_user_inactivity_timeout_left_button" />
+ <java-symbol type="string" name="demo_user_inactivity_timeout_right_button" />
<java-symbol type="layout" name="resolver_list" />
<java-symbol type="id" name="resolver_list" />
@@ -2183,6 +2201,7 @@
<java-symbol type="style" name="TextAppearance.Material.TimePicker.TimeLabel" />
<java-symbol type="attr" name="seekBarPreferenceStyle" />
<java-symbol type="style" name="Theme.DeviceDefault.Resolver" />
+ <java-symbol type="style" name="Theme.DeviceDefault.System" />
<java-symbol type="attr" name="preferenceActivityStyle" />
<java-symbol type="attr" name="preferenceFragmentStyle" />
<java-symbol type="bool" name="skipHoldBeforeMerge" />
@@ -2254,6 +2273,8 @@
<java-symbol type="string" name="prohibit_manual_network_selection_in_gobal_mode" />
<java-symbol type="id" name="profile_button" />
+ <java-symbol type="array" name="config_vvmSmsFilterRegexes" />
+
<!-- Cascading submenus -->
<java-symbol type="dimen" name="cascading_menus_min_smallest_width" />
@@ -2547,6 +2568,7 @@
<java-symbol type="color" name="notification_action_list" />
<java-symbol type="color" name="notification_material_background_color" />
+ <java-symbol type="color" name="notification_action_list_dark" />
<!-- Resolver target actions -->
<java-symbol type="array" name="resolver_target_actions_pin" />
@@ -2607,9 +2629,69 @@
<!-- Pinner Service -->
<java-symbol type="array" name="config_defaultPinnerServiceFiles" />
+ <java-symbol type="bool" name="config_pinnerCameraApp" />
+
+ <java-symbol type="string" name="config_doubleTouchGestureEnableFile" />
<java-symbol type="string" name="suspended_widget_accessibility" />
+ <!-- Used internally for assistant to launch activity transitions -->
+ <java-symbol type="id" name="cross_task_transition" />
+
+ <java-symbol type="id" name="button_holder" />
+
+ <java-symbol type="bool" name="config_useRoundIcon" />
+
+ <!-- For System navigation keys -->
+ <java-symbol type="bool" name="config_supportSystemNavigationKeys" />
+
<java-symbol type="layout" name="unsupported_display_size_dialog_content" />
<java-symbol type="string" name="unsupported_display_size_message" />
+
+ <java-symbol type="layout" name="notification_material_action_emphasized" />
+
+ <!-- Package name for the device provisioning package -->
+ <java-symbol type="string" name="config_deviceProvisioningPackage" />
+
+ <!-- Colon separated list of package names that should be granted DND access -->
+ <java-symbol type="string" name="config_defaultDndAccessPackages" />
+
+ <java-symbol type="string" name="lockscreen_storage_locked" />
+
+ <!-- Used for MimeIconUtils. -->
+ <java-symbol type="drawable" name="ic_doc_apk" />
+ <java-symbol type="drawable" name="ic_doc_audio" />
+ <java-symbol type="drawable" name="ic_doc_certificate" />
+ <java-symbol type="drawable" name="ic_doc_codes" />
+ <java-symbol type="drawable" name="ic_doc_compressed" />
+ <java-symbol type="drawable" name="ic_doc_contact" />
+ <java-symbol type="drawable" name="ic_doc_event" />
+ <java-symbol type="drawable" name="ic_doc_font" />
+ <java-symbol type="drawable" name="ic_doc_image" />
+ <java-symbol type="drawable" name="ic_doc_pdf" />
+ <java-symbol type="drawable" name="ic_doc_presentation" />
+ <java-symbol type="drawable" name="ic_doc_spreadsheet" />
+ <java-symbol type="drawable" name="ic_doc_document" />
+ <java-symbol type="drawable" name="ic_doc_video" />
+ <java-symbol type="drawable" name="ic_doc_word" />
+ <java-symbol type="drawable" name="ic_doc_excel" />
+ <java-symbol type="drawable" name="ic_doc_powerpoint" />
+ <java-symbol type="drawable" name="ic_doc_folder" />
+ <java-symbol type="drawable" name="ic_doc_audio" />
+ <java-symbol type="drawable" name="ic_doc_image" />
+ <java-symbol type="drawable" name="ic_doc_text" />
+ <java-symbol type="drawable" name="ic_doc_video" />
+ <java-symbol type="drawable" name="ic_doc_generic" />
+
+ <java-symbol type="bool" name="config_nightDisplayAvailable" />
+ <java-symbol type="bool" name="config_allowDisablingAssistDisclosure" />
+ <java-symbol type="integer" name="config_defaultNightDisplayAutoMode" />
+ <java-symbol type="integer" name="config_defaultNightDisplayCustomStartTime" />
+ <java-symbol type="integer" name="config_defaultNightDisplayCustomEndTime" />
+
+ <!-- Default first user restrictions -->
+ <java-symbol type="array" name="config_defaultFirstUserRestrictions" />
+
+ <java-symbol type="drawable" name="ic_restart" />
+
</resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index a77b951..5b2522f 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -57,6 +57,7 @@
<item name="colorPrimaryDark">@color/legacy_primary_dark</item>
<item name="colorPrimary">@color/legacy_primary</item>
+ <item name="colorSecondary">?attr/colorPrimary</item>
<item name="colorControlActivated">@color/legacy_control_activated</item>
<item name="colorControlNormal">@color/legacy_control_normal</item>
<item name="colorControlHighlight">@color/legacy_button_pressed</item>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 11bb106..0e98ade 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -199,25 +199,50 @@
<item name="mediaRouteButtonStyle">@style/Widget.DeviceDefault.MediaRouteButton</item>
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_dark</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
+
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar -->
- <style name="Theme.DeviceDefault.NoActionBar" parent="Theme.Material.NoActionBar" />
+ <style name="Theme.DeviceDefault.NoActionBar" parent="Theme.Material.NoActionBar">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_dark</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
+ </style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar. This theme
sets {@link android.R.attr#windowFullscreen} to true. -->
- <style name="Theme.DeviceDefault.NoActionBar.Fullscreen" parent="Theme.Material.NoActionBar.Fullscreen" />
+ <style name="Theme.DeviceDefault.NoActionBar.Fullscreen" parent="Theme.Material.NoActionBar.Fullscreen">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_dark</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
+ </style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
extending in to overscan region. This theme
sets {@link android.R.attr#windowFullscreen} and {@link android.R.attr#windowOverscan}
to true. -->
- <style name="Theme.DeviceDefault.NoActionBar.Overscan" parent="Theme.Material.NoActionBar.Overscan" />
+ <style name="Theme.DeviceDefault.NoActionBar.Overscan" parent="Theme.Material.NoActionBar.Overscan">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_dark</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
+ </style>
<!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent
system decor. This theme sets {@link android.R.attr#windowTranslucentStatus} and
{@link android.R.attr#windowTranslucentNavigation} to true. -->
- <style name="Theme.DeviceDefault.NoActionBar.TranslucentDecor" parent="Theme.Material.NoActionBar.TranslucentDecor" />
+ <style name="Theme.DeviceDefault.NoActionBar.TranslucentDecor" parent="Theme.Material.NoActionBar.TranslucentDecor">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_dark</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
+ </style>
<!-- DeviceDefault theme for dialog windows and activities. This changes the window to be
floating (not fill the entire screen), and puts a frame around its contents. You can set this
@@ -231,18 +256,38 @@
<item name="textAppearance">@style/TextAppearance.DeviceDefault</item>
<item name="textAppearanceInverse">@style/TextAppearance.DeviceDefault.Inverse</item>
+
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_dark</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a
regular dialog. -->
- <style name="Theme.DeviceDefault.Dialog.MinWidth" parent="Theme.Material.Dialog.MinWidth" />
+ <style name="Theme.DeviceDefault.Dialog.MinWidth" parent="Theme.Material.Dialog.MinWidth">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_dark</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
+ </style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
- <style name="Theme.DeviceDefault.Dialog.NoActionBar" parent="Theme.Material.Dialog.NoActionBar" />
+ <style name="Theme.DeviceDefault.Dialog.NoActionBar" parent="Theme.Material.Dialog.NoActionBar">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_dark</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
+ </style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width
for a regular dialog. -->
- <style name="Theme.DeviceDefault.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Dialog.NoActionBar.MinWidth" />
+ <style name="Theme.DeviceDefault.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Dialog.NoActionBar.MinWidth">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_dark</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
+ </style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
<style name="Theme.DeviceDefault.Dialog.FixedSize">
@@ -262,44 +307,99 @@
<!-- DeviceDefault theme for a window that will be displayed either full-screen on smaller
screens (small, normal) or as a dialog on larger screens (large, xlarge). -->
- <style name="Theme.DeviceDefault.DialogWhenLarge" parent="Theme.Material.DialogWhenLarge" />
+ <style name="Theme.DeviceDefault.DialogWhenLarge" parent="Theme.Material.DialogWhenLarge">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_dark</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
+ </style>
<!-- DeviceDefault theme for a window without an action bar that will be displayed either
full-screen on smaller screens (small, normal) or as a dialog on larger screens (large,
xlarge). -->
- <style name="Theme.DeviceDefault.DialogWhenLarge.NoActionBar" parent="Theme.Material.DialogWhenLarge.NoActionBar" />
+ <style name="Theme.DeviceDefault.DialogWhenLarge.NoActionBar" parent="Theme.Material.DialogWhenLarge.NoActionBar">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_dark</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
+ </style>
<!-- DeviceDefault theme for a presentation window on a secondary display. -->
- <style name="Theme.DeviceDefault.Dialog.Presentation" parent="Theme.Material.Dialog.Presentation" />
+ <style name="Theme.DeviceDefault.Dialog.Presentation" parent="Theme.Material.Dialog.Presentation">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_dark</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
+ </style>
<!-- DeviceDefault theme for panel windows. This removes all extraneous window
decorations, so you basically have an empty rectangle in which to place your content. It makes
the window floating, with a transparent background, and turns off dimming behind the window. -->
- <style name="Theme.DeviceDefault.Panel" parent="Theme.Material.Panel" />
+ <style name="Theme.DeviceDefault.Panel" parent="Theme.Material.Panel">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_dark</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
+ </style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
behind them. -->
- <style name="Theme.DeviceDefault.Wallpaper" parent="Theme.Material.Wallpaper" />
+ <style name="Theme.DeviceDefault.Wallpaper" parent="Theme.Material.Wallpaper">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_dark</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
+ </style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
behind them and without an action bar. -->
- <style name="Theme.DeviceDefault.Wallpaper.NoTitleBar" parent="Theme.Material.Wallpaper.NoTitleBar" />
+ <style name="Theme.DeviceDefault.Wallpaper.NoTitleBar" parent="Theme.Material.Wallpaper.NoTitleBar">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_dark</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
+ </style>
<!-- DeviceDefault style for input methods, which is used by the
{@link android.inputmethodservice.InputMethodService} class.-->
- <style name="Theme.DeviceDefault.InputMethod" parent="Theme.Material.InputMethod" />
+ <style name="Theme.DeviceDefault.InputMethod" parent="Theme.Material.InputMethod">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
<!-- DeviceDefault style for input methods, which is used by the
{@link android.service.voice.VoiceInteractionSession} class.-->
- <style name="Theme.DeviceDefault.VoiceInteractionSession" parent="Theme.Material.VoiceInteractionSession" >
-
+ <style name="Theme.DeviceDefault.VoiceInteractionSession" parent="Theme.Material.VoiceInteractionSession">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
</style>
+
<style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
<item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault</item>
+
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_dark</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
</style>
- <style name="Theme.DeviceDefault.SearchBar" parent="Theme.Material.SearchBar" />
- <style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame" />
+ <style name="Theme.DeviceDefault.SearchBar" parent="Theme.Material.SearchBar">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_dark</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
+ </style>
+
+ <style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_dark</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
+ </style>
<!-- Variant of {@link #Theme_DeviceDefault} with a light-colored style -->
<style name="Theme.DeviceDefault.Light" parent="Theme.Material.Light" >
@@ -447,34 +547,63 @@
<item name="mediaRouteButtonStyle">@style/Widget.DeviceDefault.Light.MediaRouteButton</item>
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
</style>
<!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an
inverse color profile. -->
- <style name="Theme.DeviceDefault.Light.DarkActionBar" parent="Theme.Material.Light.DarkActionBar" />
+ <style name="Theme.DeviceDefault.Light.DarkActionBar" parent="Theme.Material.Light.DarkActionBar">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_dark</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar -->
- <style name="Theme.DeviceDefault.Light.NoActionBar" parent="Theme.Material.Light.NoActionBar" />
+ <style name="Theme.DeviceDefault.Light.NoActionBar" parent="Theme.Material.Light.NoActionBar">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar.
This theme sets {@link android.R.attr#windowFullscreen} to true. -->
- <style name="Theme.DeviceDefault.Light.NoActionBar.Fullscreen" parent="Theme.Material.Light.NoActionBar.Fullscreen" />
+ <style name="Theme.DeviceDefault.Light.NoActionBar.Fullscreen" parent="Theme.Material.Light.NoActionBar.Fullscreen">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar
and extending in to overscan region. This theme
sets {@link android.R.attr#windowFullscreen} and {@link android.R.attr#windowOverscan}
to true. -->
- <style name="Theme.DeviceDefault.Light.NoActionBar.Overscan" parent="Theme.Material.Light.NoActionBar.Overscan" />
+ <style name="Theme.DeviceDefault.Light.NoActionBar.Overscan" parent="Theme.Material.Light.NoActionBar.Overscan">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent
system decor. This theme sets {@link android.R.attr#windowTranslucentStatus} and
{@link android.R.attr#windowTranslucentNavigation} to true. -->
- <style name="Theme.DeviceDefault.Light.NoActionBar.TranslucentDecor" parent="Theme.Material.Light.NoActionBar.TranslucentDecor" />
+ <style name="Theme.DeviceDefault.Light.NoActionBar.TranslucentDecor" parent="Theme.Material.Light.NoActionBar.TranslucentDecor">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
<!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be
floating (not fill the entire screen), and puts a frame around its contents. You can set this
theme on an activity if you would like to make an activity that looks like a Dialog.-->
- <style name="Theme.DeviceDefault.Light.Dialog" parent="Theme.Material.Light.Dialog" >
+ <style name="Theme.DeviceDefault.Light.Dialog" parent="Theme.Material.Light.Dialog">
<item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault.Light</item>
<item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
@@ -483,18 +612,38 @@
<item name="textAppearance">@style/TextAppearance.DeviceDefault</item>
<item name="textAppearanceInverse">@style/TextAppearance.DeviceDefault.Inverse</item>
+
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} that has a nice minimum width for a
regular dialog. -->
- <style name="Theme.DeviceDefault.Light.Dialog.MinWidth" parent="Theme.Material.Light.Dialog.MinWidth" />
+ <style name="Theme.DeviceDefault.Light.Dialog.MinWidth" parent="Theme.Material.Light.Dialog.MinWidth">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
- <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar" parent="Theme.Material.Light.Dialog.NoActionBar" />
+ <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar" parent="Theme.Material.Light.Dialog.NoActionBar">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum
width for a regular dialog. -->
- <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Light.Dialog.NoActionBar.MinWidth" />
+ <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Light.Dialog.NoActionBar.MinWidth">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
<style name="Theme.DeviceDefault.Light.Dialog.FixedSize">
@@ -502,6 +651,11 @@
<item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
<item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
<item name="windowFixedHeightMinor">@dimen/dialog_fixed_height_minor</item>
+
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. -->
@@ -510,33 +664,125 @@
<item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
<item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
<item name="windowFixedHeightMinor">@dimen/dialog_fixed_height_minor</item>
+
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
</style>
<!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller
screens (small, normal) or as a dialog on larger screens (large, xlarge). -->
- <style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="Theme.Material.Light.DialogWhenLarge" />
+ <style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="Theme.Material.Light.DialogWhenLarge">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
<!-- DeviceDefault light theme for a window without an action bar that will be displayed either
full-screen on smaller screens (small, normal) or as a dialog on larger screens (large,
xlarge). -->
- <style name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar" parent="Theme.Material.Light.DialogWhenLarge.NoActionBar" />
+ <style name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar" parent="Theme.Material.Light.DialogWhenLarge.NoActionBar">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
<!-- DeviceDefault light theme for a presentation window on a secondary display. -->
- <style name="Theme.DeviceDefault.Light.Dialog.Presentation" parent="Theme.Material.Light.Dialog.Presentation" />
+ <style name="Theme.DeviceDefault.Light.Dialog.Presentation" parent="Theme.Material.Light.Dialog.Presentation">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
<!-- DeviceDefault light theme for panel windows. This removes all extraneous window
decorations, so you basically have an empty rectangle in which to place your content. It makes
the window floating, with a transparent background, and turns off dimming behind the window. -->
- <style name="Theme.DeviceDefault.Light.Panel" parent="Theme.Material.Light.Panel" />
+ <style name="Theme.DeviceDefault.Light.Panel" parent="Theme.Material.Light.Panel">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
<style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert">
<item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault.Light</item>
+
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
</style>
- <style name="Theme.DeviceDefault.Light.SearchBar" parent="Theme.Material.Light.SearchBar" />
+ <style name="Theme.DeviceDefault.Light.SearchBar" parent="Theme.Material.Light.SearchBar">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
+
+ <style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
<!-- DeviceDefault theme for a window that should look like the Settings app. -->
- <style name="Theme.DeviceDefault.Settings" parent="Theme.Material.Settings" />
+ <style name="Theme.DeviceDefault.Settings" parent="Theme.Material.Settings">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_settings</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
+ <item name="colorSecondary">@color/secondary_device_default_settings</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
+
+ <!-- DeviceDefault theme for a window that should use Settings theme colors but has
+ a full dark palette (instead of Light with dark action bar like
+ Theme.DeviceDefault.Settings. -->
+ <style name="Theme.DeviceDefault.Settings.Dark" parent="Theme.Material">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_settings</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
+ <item name="colorSecondary">@color/secondary_device_default_settings</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
+ </style>
+
+ <!-- Variant of {@link #Theme_DeviceDefault_Settings_Dark} with no action bar -->
+ <style name="Theme.DeviceDefault.Settings.Dark.NoActionBar" parent="Theme.Material.NoActionBar">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_settings</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
+ <item name="colorSecondary">@color/secondary_device_default_settings</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
+ </style>
+
+ <style name="Theme.DeviceDefault.Settings.Dialog" parent="Theme.Material.Settings.Dialog">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_settings</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
+ <item name="colorSecondary">@color/secondary_device_default_settings</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
+
+ <style name="Theme.DeviceDefault.Settings.DialogWhenLarge" parent="Theme.Material.Settings.DialogWhenLarge">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_settings</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
+ <item name="colorSecondary">@color/secondary_device_default_settings</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
+
+ <style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert">
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_settings</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
+ <item name="colorSecondary">@color/secondary_device_default_settings</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
<!-- Theme used for the intent picker activity. -->
<style name="Theme.DeviceDefault.Resolver" parent="Theme.Material.Light">
@@ -549,6 +795,28 @@
<item name="colorControlActivated">?attr/colorControlHighlight</item>
<item name="listPreferredItemPaddingStart">?attr/dialogPreferredPadding</item>
<item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item>
+
+ <!-- Color palette -->
+ <item name="colorPrimary">@color/primary_device_default_light</item>
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
+
+ <!-- DeviceDefault theme for the default system theme. -->
+ <style name="Theme.DeviceDefault.System" parent="Theme.DeviceDefault.Light.DarkActionBar" />
+
+ <style name="ThemeOverlay.DeviceDefault" />
+
+ <style name="ThemeOverlay.DeviceDefault.Accent">
+ <item name="colorAccent">@color/accent_device_default_dark</item>
+ </style>
+
+ <style name="ThemeOverlay.DeviceDefault.Accent.Light">
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ </style>
+
+ <style name="ThemeOverlay.DeviceDefault.Dark.ActionBar.Accent" parent="ThemeOverlay.Material.Dark.ActionBar">
+ <item name="colorAccent">@color/accent_device_default_dark</item>
</style>
</resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 2ea5c5e..7e2867d 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -119,8 +119,8 @@
<item name="textAppearanceListItemSecondary">@style/TextAppearance.Material.Body1</item>
<item name="listPreferredItemPaddingLeft">@dimen/list_item_padding_horizontal_material</item>
<item name="listPreferredItemPaddingRight">@dimen/list_item_padding_horizontal_material</item>
- <item name="listPreferredItemPaddingStart">@dimen/list_item_padding_horizontal_material</item>
- <item name="listPreferredItemPaddingEnd">@dimen/list_item_padding_horizontal_material</item>
+ <item name="listPreferredItemPaddingStart">@dimen/list_item_padding_start_material</item>
+ <item name="listPreferredItemPaddingEnd">@dimen/list_item_padding_end_material</item>
<!-- @hide -->
<item name="searchResultListItemHeight">58dip</item>
@@ -153,9 +153,9 @@
<item name="windowBackground">?attr/colorBackground</item>
<item name="windowClipToOutline">true</item>
<item name="windowFrame">@null</item>
- <item name="windowNoTitle">false</item>
+ <item name="windowNoTitle">@bool/config_windowNoTitleDefault</item>
<item name="windowFullscreen">false</item>
- <item name="windowOverscan">false</item>
+ <item name="windowOverscan">@bool/config_windowOverscanByDefault</item>
<item name="windowIsFloating">false</item>
<item name="windowContentOverlay">@null</item>
<item name="windowShowWallpaper">false</item>
@@ -164,7 +164,7 @@
<item name="windowTitleBackgroundStyle">@style/WindowTitleBackground.Material</item>
<item name="windowAnimationStyle">@style/Animation.Material.Activity</item>
<item name="windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
- <item name="windowActionBar">true</item>
+ <item name="windowActionBar">@bool/config_windowActionBarSupported</item>
<item name="windowActionModeOverlay">false</item>
<item name="windowDrawsSystemBarBackgrounds">true</item>
<item name="windowActionBarFullscreenDecorLayout">@layout/screen_toolbar</item>
@@ -370,7 +370,7 @@
<item name="timePickerStyle">@style/Widget.Material.TimePicker</item>
<!-- TimePicker dialog theme -->
- <item name="timePickerDialogTheme">?attr/dialogTheme</item>
+ <item name="timePickerDialogTheme">@style/ThemeOverlay.Material.Dialog.TimePicker</item>
<!-- DatePicker style -->
<item name="datePickerStyle">@style/Widget.Material.DatePicker</item>
@@ -480,8 +480,8 @@
<item name="textAppearanceListItemSecondary">@style/TextAppearance.Material.Body1</item>
<item name="listPreferredItemPaddingLeft">@dimen/list_item_padding_horizontal_material</item>
<item name="listPreferredItemPaddingRight">@dimen/list_item_padding_horizontal_material</item>
- <item name="listPreferredItemPaddingStart">@dimen/list_item_padding_horizontal_material</item>
- <item name="listPreferredItemPaddingEnd">@dimen/list_item_padding_horizontal_material</item>
+ <item name="listPreferredItemPaddingStart">@dimen/list_item_padding_start_material</item>
+ <item name="listPreferredItemPaddingEnd">@dimen/list_item_padding_end_material</item>
<!-- @hide -->
<item name="searchResultListItemHeight">58dip</item>
@@ -514,9 +514,9 @@
<item name="windowBackground">?attr/colorBackground</item>
<item name="windowClipToOutline">true</item>
<item name="windowFrame">@null</item>
- <item name="windowNoTitle">false</item>
+ <item name="windowNoTitle">@bool/config_windowNoTitleDefault</item>
<item name="windowFullscreen">false</item>
- <item name="windowOverscan">false</item>
+ <item name="windowOverscan">@bool/config_windowOverscanByDefault</item>
<item name="windowIsFloating">false</item>
<item name="windowContentOverlay">@null</item>
<item name="windowShowWallpaper">false</item>
@@ -525,7 +525,7 @@
<item name="windowTitleBackgroundStyle">@style/WindowTitleBackground.Material</item>
<item name="windowAnimationStyle">@style/Animation.Material.Activity</item>
<item name="windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
- <item name="windowActionBar">true</item>
+ <item name="windowActionBar">@bool/config_windowActionBarSupported</item>
<item name="windowActionModeOverlay">false</item>
<item name="windowDrawsSystemBarBackgrounds">true</item>
<item name="windowActionBarFullscreenDecorLayout">@layout/screen_toolbar</item>
@@ -733,7 +733,7 @@
<item name="timePickerStyle">@style/Widget.Material.Light.TimePicker</item>
<!-- TimePicker dialog theme -->
- <item name="timePickerDialogTheme">?attr/dialogTheme</item>
+ <item name="timePickerDialogTheme">@style/ThemeOverlay.Material.Dialog.TimePicker</item>
<!-- DatePicker style -->
<item name="datePickerStyle">@style/Widget.Material.Light.DatePicker</item>
@@ -864,11 +864,8 @@
<item name="searchViewStyle">@style/Widget.Material.SearchView.ActionBar</item>
</style>
- <!-- Theme overlay that overrides window properties to display as a dialog. -->
- <style name="ThemeOverlay.Material.Dialog">
- <item name="colorBackgroundCacheHint">@null</item>
- <item name="colorBackground">?attr/colorBackgroundFloating</item>
-
+ <!-- Base theme for overlay dialogs, customize the colours in the actual dialog theme. -->
+ <style name="ThemeOverlay.Material.BaseDialog">
<item name="windowFrame">@null</item>
<item name="windowTitleStyle">@style/DialogWindowTitle.Material</item>
<item name="windowTitleBackgroundStyle">@style/DialogWindowTitleBackground.Material</item>
@@ -897,6 +894,17 @@
<item name="windowFixedHeightMinor">@null</item>
</style>
+ <!-- Theme overlay that overrides window properties to display as a dialog. -->
+ <style name="ThemeOverlay.Material.Dialog" parent="ThemeOverlay.Material.BaseDialog">
+ <item name="colorBackgroundCacheHint">@null</item>
+ <item name="colorBackground">?attr/colorBackgroundFloating</item>
+ </style>
+
+ <!-- Theme overlay that overrides window properties to display as a time picker dialog. -->
+ <style name="ThemeOverlay.Material.Dialog.TimePicker">
+ <item name="alertDialogStyle">@style/TimePickerDialog.Material</item>
+ </style>
+
<!-- Theme overlay that overrides window properties to display as a date picker dialog. -->
<style name="ThemeOverlay.Material.Dialog.DatePicker">
<item name="alertDialogStyle">@style/DatePickerDialog.Material</item>
@@ -1090,10 +1098,10 @@
<item name="colorBackgroundCacheHint">@null</item>
- <item name="listPreferredItemPaddingLeft">24dip</item>
- <item name="listPreferredItemPaddingRight">24dip</item>
- <item name="listPreferredItemPaddingStart">24dip</item>
- <item name="listPreferredItemPaddingEnd">24dip</item>
+ <item name="listPreferredItemPaddingLeft">?attr/dialogPreferredPadding</item>
+ <item name="listPreferredItemPaddingRight">?attr/dialogPreferredPadding</item>
+ <item name="listPreferredItemPaddingStart">?attr/dialogPreferredPadding</item>
+ <item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item>
<item name="listDivider">@null</item>
@@ -1201,10 +1209,10 @@
<item name="colorBackgroundCacheHint">@null</item>
- <item name="listPreferredItemPaddingLeft">24dip</item>
- <item name="listPreferredItemPaddingRight">24dip</item>
- <item name="listPreferredItemPaddingStart">24dip</item>
- <item name="listPreferredItemPaddingEnd">24dip</item>
+ <item name="listPreferredItemPaddingLeft">?attr/dialogPreferredPadding</item>
+ <item name="listPreferredItemPaddingRight">?attr/dialogPreferredPadding</item>
+ <item name="listPreferredItemPaddingStart">?attr/dialogPreferredPadding</item>
+ <item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item>
<item name="listDivider">@null</item>
@@ -1300,8 +1308,9 @@
<!-- Default theme for Settings and activities launched from Settings. -->
<style name="Theme.Material.Settings" parent="Theme.Material.Light.DarkActionBar">
- <item name="colorPrimary">@color/material_blue_grey_900</item>
- <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
+ <item name="colorPrimary">@color/primary_material_settings</item>
+ <item name="colorPrimaryDark">@color/primary_dark_material_settings</item>
+ <item name="colorSecondary">@color/secondary_material_settings</item>
<item name="presentationTheme">@style/Theme.Material.Settings.Dialog.Presentation</item>
<item name="searchDialogTheme">@style/Theme.Material.Settings.SearchBar</item>
@@ -1310,8 +1319,9 @@
<!-- Default theme for Settings and activities launched from Settings. -->
<style name="Theme.Material.Settings.NoActionBar" parent="Theme.Material.Light.NoActionBar">
- <item name="colorPrimary">@color/material_blue_grey_900</item>
- <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
+ <item name="colorPrimary">@color/primary_material_settings</item>
+ <item name="colorPrimaryDark">@color/primary_dark_material_settings</item>
+ <item name="colorSecondary">@color/secondary_material_settings</item>
<item name="presentationTheme">@style/Theme.Material.Settings.Dialog.Presentation</item>
<item name="searchDialogTheme">@style/Theme.Material.Settings.SearchBar</item>
@@ -1319,41 +1329,48 @@
</style>
<style name="Theme.Material.Settings.BaseDialog" parent="Theme.Material.Light.BaseDialog">
- <item name="colorPrimary">@color/material_blue_grey_900</item>
- <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
+ <item name="colorPrimary">@color/primary_material_settings</item>
+ <item name="colorPrimaryDark">@color/primary_dark_material_settings</item>
+ <item name="colorSecondary">@color/secondary_material_settings</item>
</style>
<style name="Theme.Material.Settings.Dialog" parent="Theme.Material.Settings.BaseDialog" />
<style name="Theme.Material.Settings.Dialog.BaseAlert" parent="Theme.Material.Light.Dialog.BaseAlert">
- <item name="colorPrimary">@color/material_blue_grey_900</item>
- <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
+ <item name="colorPrimary">@color/primary_material_settings</item>
+ <item name="colorPrimaryDark">@color/primary_dark_material_settings</item>
+ <item name="colorSecondary">@color/secondary_material_settings</item>
</style>
<style name="Theme.Material.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.BaseAlert" />
<style name="Theme.Material.Settings.DialogWhenLarge" parent="Theme.Material.Light.DialogWhenLarge.DarkActionBar">
- <item name="colorPrimary">@color/material_blue_grey_900</item>
- <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
+ <item name="colorPrimary">@color/primary_material_settings</item>
+ <item name="colorPrimaryDark">@color/primary_dark_material_settings</item>
+ <item name="colorSecondary">@color/secondary_material_settings</item>
</style>
<style name="Theme.Material.Settings.DialogWhenLarge.NoActionBar" parent="Theme.Material.Light.DialogWhenLarge.NoActionBar">
- <item name="colorPrimary">@color/material_blue_grey_900</item>
- <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
+ <item name="colorPrimary">@color/primary_material_settings</item>
+ <item name="colorPrimaryDark">@color/primary_dark_material_settings</item>
+ <item name="colorSecondary">@color/secondary_material_settings</item>
</style>
<style name="Theme.Material.Settings.Dialog.Presentation" parent="Theme.Material.Light.Dialog.Presentation">
- <item name="colorPrimary">@color/material_blue_grey_900</item>
- <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
+ <item name="colorPrimary">@color/primary_material_settings</item>
+ <item name="colorPrimaryDark">@color/primary_dark_material_settings</item>
+ <item name="colorSecondary">@color/secondary_material_settings</item>
</style>
<style name="Theme.Material.Settings.SearchBar" parent="Theme.Material.Light.SearchBar">
- <item name="colorPrimary">@color/material_blue_grey_900</item>
- <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
+ <item name="colorPrimary">@color/primary_material_settings</item>
+ <item name="colorPrimaryDark">@color/primary_dark_material_settings</item>
+ <item name="colorSecondary">@color/secondary_material_settings</item>
</style>
<style name="Theme.Material.Settings.CompactMenu" parent="Theme.Material.Light.CompactMenu">
- <item name="colorPrimary">@color/material_blue_grey_900</item>
- <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
+ <item name="colorPrimary">@color/primary_material_settings</item>
+ <item name="colorPrimaryDark">@color/primary_dark_material_settings</item>
+ <item name="colorSecondary">@color/secondary_material_settings</item>
</style>
</resources>
diff --git a/core/res/res/values/themes_micro.xml b/core/res/res/values/themes_micro.xml
deleted file mode 100644
index 25a6e00..0000000
--- a/core/res/res/values/themes_micro.xml
+++ /dev/null
@@ -1,100 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2014 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.
--->
-<resources>
- <style name="Theme.MicroBase" parent="Theme.Material.NoActionBar">
- <item name="alertDialogTheme">@style/Theme.Micro.Dialog.Alert</item>
- <item name="alertDialogStyle">@style/AlertDialog.Micro</item>
- <item name="dialogTheme">@style/Theme.Micro.Dialog</item>
- <item name="textViewStyle">@style/Widget.Micro.TextView</item>
- <item name="numberPickerStyle">@style/Widget.Micro.NumberPicker</item>
- <item name="windowAnimationStyle">@style/Animation.Micro.Activity</item>
- <item name="windowBackground">@color/black</item>
- <item name="windowContentOverlay">@null</item>
- <item name="windowIsFloating">false</item>
- <!-- Required to force windowInsets dispatch through application UI. -->
- <item name="windowOverscan">true</item>
- </style>
-
- <style name="Theme.Micro" parent="Theme.MicroBase">
- </style>
-
- <style name="Theme.Micro.LightBase" parent="Theme.Material.Light.NoActionBar">
- <item name="alertDialogTheme">@style/Theme.Micro.Dialog.Alert</item>
- <item name="alertDialogStyle">@style/AlertDialog.Micro</item>
- <item name="dialogTheme">@style/Theme.Micro.Dialog</item>
- <item name="textViewStyle">@style/Widget.Micro.TextView</item>
- <item name="numberPickerStyle">@style/Widget.Micro.NumberPicker</item>
- <item name="windowAnimationStyle">@style/Animation.Micro.Activity</item>
- <item name="windowBackground">@color/white</item>
- <item name="windowContentOverlay">@null</item>
- <item name="windowIsFloating">false</item>
- <!-- Required to force windowInsets dispatch through application UI. -->
- <item name="windowOverscan">true</item>
- </style>
-
- <!-- Indirection needed for overlays to make sure there is a common base parent -->
- <style name="Theme.Micro.Light" parent="Theme.Micro.LightBase">
- </style>
-
- <style name="Theme.Micro.DialogBase" parent="Theme.Material.Light.Dialog">
- <item name="windowTitleStyle">@android:style/DialogWindowTitle.Micro</item>
- <item name="windowIsFloating">false</item>
- <item name="windowFullscreen">true</item>
- <item name="textAppearance">@style/TextAppearance.Micro</item>
- <item name="textAppearanceInverse">@style/TextAppearance.Micro</item>
- <!-- Required to force windowInsets dispatch through application UI. -->
- <item name="windowOverscan">true</item>
- </style>
-
- <!-- Indirection needed for overlays to make sure there is a common base parent -->
- <style name="Theme.Micro.Dialog" parent="Theme.Micro.DialogBase">
- </style>
-
- <style name="Theme.Micro.Dialog.Alert">
- <item name="windowTitleStyle">@style/DialogWindowTitle.Micro</item>
- <item name="alertDialogStyle">@style/AlertDialog.Micro</item>
- <item name="windowIsFloating">false</item>
- <item name="windowBackground">@android:color/transparent</item>
- <item name="windowOverscan">true</item>
- <item name="windowContentOverlay">@null</item>
- <item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
- <item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
- </style>
-
- <style name="Theme.Micro.Dialog.AppError" parent="Theme.Micro.Dialog">
- <item name="windowBackground">@null</item>
- <item name="alertDialogStyle">@style/AlertDialog.Micro</item>
- <item name="windowOverscan">true</item>
- <item name="windowCloseOnTouchOutside">false</item>
- <item name="textSize">20sp</item>
- <item name="fontFamily">sans-serif-condensed-light</item>
- <item name="textColor">@color/micro_text_light</item>
- </style>
-
- <style name="Theme.Micro.Panel" parent="Theme.Material.Panel" />
- <style name="Theme.Micro.Light.Panel" parent="Theme.Material.Light.Panel" />
-
- <!-- Default theme for material style input methods, which is used by the
- {@link android.inputmethodservice.InputMethodService} class.
- This inherits from Theme.Panel, but sets up IME appropriate animations
- and a few custom attributes. -->
- <style name="Theme.Micro.InputMethod" parent="Theme.Micro.Panel">
- <item name="windowAnimationStyle">@style/Animation.InputMethod</item>
- <item name="imeFullscreenBackground">#1e282c</item>
- <item name="imeExtractEnterAnimation">@anim/input_method_extract_enter</item>
- <item name="imeExtractExitAnimation">@anim/input_method_extract_exit</item>
- </style>
-</resources>
diff --git a/core/res/res/xml-watch/default_zen_mode_config.xml b/core/res/res/xml-watch/default_zen_mode_config.xml
new file mode 100644
index 0000000..26af10c
--- /dev/null
+++ b/core/res/res/xml-watch/default_zen_mode_config.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<!-- Default configuration for zen mode. See android.service.notification.ZenModeConfig. -->
+<zen version="2">
+ <!-- Allow starred contacts to go through only. Repeated calls on.
+ Calls, messages, reminders, events off.-->
+ <allow from="2" repeatCallers="true" calls="false" messages="false" reminders="false"
+ events="false"/>
+</zen>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index c02a01a..b357016 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -78,7 +78,7 @@
<shortcode country="cz" premium="9\\d{6,7}" free="116\\d{3}" />
<!-- Germany: 4-5 digits plus 1232xxx (premium codes from http://www.vodafone.de/infofaxe/537.pdf and http://premiumdienste.eplus.de/pdf/kodex.pdf), plus EU. To keep the premium regex from being too large, it only includes payment processors that have been used by SMS malware, with the regular pattern matching the other premium short codes. -->
- <shortcode country="de" pattern="\\d{4,5}|1232\\d{3}" premium="11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999" free="116\\d{3}|81214|81215|47529|70296" />
+ <shortcode country="de" pattern="\\d{4,5}|1232\\d{3}" premium="11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999" free="116\\d{3}|81214|81215|47529|70296|73240|3011" />
<!-- Denmark: see http://iprs.webspacecommerce.com/Denmark-Premium-Rate-Numbers -->
<shortcode country="dk" pattern="\\d{4,5}" premium="1\\d{3}" free="116\\d{3}|4665" />
@@ -153,6 +153,9 @@
<!-- Latvia: 4 digits, known premium codes listed, plus EU -->
<shortcode country="lv" pattern="\\d{4}" premium="18(?:19|63|7[1-4])" free="116\\d{3}" />
+ <!-- Macedonia -->
+ <shortcode country="mk" free="129005|122" />
+
<!-- Mexico: 4-5 digits (not confirmed), known premium codes listed -->
<shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="46645" />
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
index 755e7c4..31ce95e 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
@@ -35,6 +35,7 @@
/** The amount of time to sleep between issuing start/stop SCO in ms. */
private static final long SCO_SLEEP_TIME = 2 * 1000;
+ private BluetoothAdapter mAdapter;
private BluetoothTestUtils mTestUtils;
@Override
@@ -42,13 +43,18 @@
super.setUp();
Context context = getInstrumentation().getTargetContext();
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
mTestUtils = new BluetoothTestUtils(context, TAG, OUTPUT_FILE);
+
+ // Start all tests in a disabled state.
+ if (mAdapter.isEnabled()) {
+ mTestUtils.disable(mAdapter);
+ }
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
-
mTestUtils.close();
}
@@ -61,13 +67,10 @@
return;
}
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- mTestUtils.disable(adapter);
-
for (int i = 0; i < iterations; i++) {
mTestUtils.writeOutput("enable iteration " + (i + 1) + " of " + iterations);
- mTestUtils.enable(adapter);
- mTestUtils.disable(adapter);
+ mTestUtils.enable(mAdapter);
+ mTestUtils.disable(mAdapter);
}
}
@@ -80,18 +83,14 @@
return;
}
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- mTestUtils.disable(adapter);
- mTestUtils.enable(adapter);
- mTestUtils.undiscoverable(adapter);
+ mTestUtils.enable(mAdapter);
+ mTestUtils.undiscoverable(mAdapter);
for (int i = 0; i < iterations; i++) {
mTestUtils.writeOutput("discoverable iteration " + (i + 1) + " of " + iterations);
- mTestUtils.discoverable(adapter);
- mTestUtils.undiscoverable(adapter);
+ mTestUtils.discoverable(mAdapter);
+ mTestUtils.undiscoverable(mAdapter);
}
-
- mTestUtils.disable(adapter);
}
/**
@@ -103,18 +102,14 @@
return;
}
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- mTestUtils.disable(adapter);
- mTestUtils.enable(adapter);
- mTestUtils.stopScan(adapter);
+ mTestUtils.enable(mAdapter);
+ mTestUtils.stopScan(mAdapter);
for (int i = 0; i < iterations; i++) {
mTestUtils.writeOutput("scan iteration " + (i + 1) + " of " + iterations);
- mTestUtils.startScan(adapter);
- mTestUtils.stopScan(adapter);
+ mTestUtils.startScan(mAdapter);
+ mTestUtils.stopScan(mAdapter);
}
-
- mTestUtils.disable(adapter);
}
/**
@@ -125,19 +120,16 @@
if (iterations == 0) {
return;
}
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- mTestUtils.disable(adapter);
- mTestUtils.enable(adapter);
- mTestUtils.disablePan(adapter);
+
+ mTestUtils.enable(mAdapter);
+ mTestUtils.disablePan(mAdapter);
for (int i = 0; i < iterations; i++) {
mTestUtils.writeOutput("testEnablePan iteration " + (i + 1) + " of "
+ iterations);
- mTestUtils.enablePan(adapter);
- mTestUtils.disablePan(adapter);
+ mTestUtils.enablePan(mAdapter);
+ mTestUtils.disablePan(mAdapter);
}
-
- mTestUtils.disable(adapter);
}
/**
@@ -152,19 +144,16 @@
return;
}
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
- mTestUtils.disable(adapter);
- mTestUtils.enable(adapter);
- mTestUtils.unpair(adapter, device);
+ BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
+ mTestUtils.enable(mAdapter);
+ mTestUtils.unpair(mAdapter, device);
for (int i = 0; i < iterations; i++) {
mTestUtils.writeOutput("pair iteration " + (i + 1) + " of " + iterations);
- mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey,
+ mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
BluetoothTestRunner.sDevicePairPin);
- mTestUtils.unpair(adapter, device);
+ mTestUtils.unpair(mAdapter, device);
}
- mTestUtils.disable(adapter);
}
/**
@@ -178,19 +167,16 @@
if (iterations == 0) {
return;
}
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
- mTestUtils.disable(adapter);
- mTestUtils.enable(adapter);
- mTestUtils.unpair(adapter, device);
+ BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
+ mTestUtils.enable(mAdapter);
+ mTestUtils.unpair(mAdapter, device);
for (int i = 0; i < iterations; i++) {
mTestUtils.writeOutput("acceptPair iteration " + (i + 1) + " of " + iterations);
- mTestUtils.acceptPair(adapter, device, BluetoothTestRunner.sDevicePairPasskey,
+ mTestUtils.acceptPair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
BluetoothTestRunner.sDevicePairPin);
- mTestUtils.unpair(adapter, device);
+ mTestUtils.unpair(mAdapter, device);
}
- mTestUtils.disable(adapter);
}
/**
@@ -205,25 +191,22 @@
return;
}
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
- mTestUtils.disable(adapter);
- mTestUtils.enable(adapter);
- mTestUtils.unpair(adapter, device);
- mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey,
+ BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
+ mTestUtils.enable(mAdapter);
+ mTestUtils.unpair(mAdapter, device);
+ mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
BluetoothTestRunner.sDevicePairPin);
- mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.A2DP, null);
+ mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.A2DP, null);
for (int i = 0; i < iterations; i++) {
mTestUtils.writeOutput("connectA2dp iteration " + (i + 1) + " of " + iterations);
- mTestUtils.connectProfile(adapter, device, BluetoothProfile.A2DP,
+ mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.A2DP,
String.format("connectA2dp(device=%s)", device));
- mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.A2DP,
+ mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.A2DP,
String.format("disconnectA2dp(device=%s)", device));
}
- mTestUtils.unpair(adapter, device);
- mTestUtils.disable(adapter);
+ mTestUtils.unpair(mAdapter, device);
}
/**
@@ -238,25 +221,22 @@
return;
}
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
- mTestUtils.disable(adapter);
- mTestUtils.enable(adapter);
- mTestUtils.unpair(adapter, device);
- mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey,
+ BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
+ mTestUtils.enable(mAdapter);
+ mTestUtils.unpair(mAdapter, device);
+ mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
BluetoothTestRunner.sDevicePairPin);
- mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET, null);
+ mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HEADSET, null);
for (int i = 0; i < iterations; i++) {
mTestUtils.writeOutput("connectHeadset iteration " + (i + 1) + " of " + iterations);
- mTestUtils.connectProfile(adapter, device, BluetoothProfile.HEADSET,
+ mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.HEADSET,
String.format("connectHeadset(device=%s)", device));
- mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET,
+ mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HEADSET,
String.format("disconnectHeadset(device=%s)", device));
}
- mTestUtils.unpair(adapter, device);
- mTestUtils.disable(adapter);
+ mTestUtils.unpair(mAdapter, device);
}
/**
@@ -271,25 +251,22 @@
return;
}
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
- mTestUtils.disable(adapter);
- mTestUtils.enable(adapter);
- mTestUtils.unpair(adapter, device);
- mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey,
+ BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
+ mTestUtils.enable(mAdapter);
+ mTestUtils.unpair(mAdapter, device);
+ mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
BluetoothTestRunner.sDevicePairPin);
- mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.INPUT_DEVICE, null);
+ mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.INPUT_DEVICE, null);
for (int i = 0; i < iterations; i++) {
mTestUtils.writeOutput("connectInput iteration " + (i + 1) + " of " + iterations);
- mTestUtils.connectProfile(adapter, device, BluetoothProfile.INPUT_DEVICE,
+ mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.INPUT_DEVICE,
String.format("connectInput(device=%s)", device));
- mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.INPUT_DEVICE,
+ mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.INPUT_DEVICE,
String.format("disconnectInput(device=%s)", device));
}
- mTestUtils.unpair(adapter, device);
- mTestUtils.disable(adapter);
+ mTestUtils.unpair(mAdapter, device);
}
/**
@@ -304,22 +281,19 @@
return;
}
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
- mTestUtils.disable(adapter);
- mTestUtils.enable(adapter);
- mTestUtils.unpair(adapter, device);
- mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey,
+ BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
+ mTestUtils.enable(mAdapter);
+ mTestUtils.unpair(mAdapter, device);
+ mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
BluetoothTestRunner.sDevicePairPin);
for (int i = 0; i < iterations; i++) {
mTestUtils.writeOutput("connectPan iteration " + (i + 1) + " of " + iterations);
- mTestUtils.connectPan(adapter, device);
- mTestUtils.disconnectPan(adapter, device);
+ mTestUtils.connectPan(mAdapter, device);
+ mTestUtils.disconnectPan(mAdapter, device);
}
- mTestUtils.unpair(adapter, device);
- mTestUtils.disable(adapter);
+ mTestUtils.unpair(mAdapter, device);
}
/**
@@ -334,26 +308,23 @@
return;
}
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
- mTestUtils.disable(adapter);
- mTestUtils.enable(adapter);
- mTestUtils.disablePan(adapter);
- mTestUtils.enablePan(adapter);
- mTestUtils.unpair(adapter, device);
- mTestUtils.acceptPair(adapter, device, BluetoothTestRunner.sDevicePairPasskey,
+ BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
+ mTestUtils.enable(mAdapter);
+ mTestUtils.disablePan(mAdapter);
+ mTestUtils.enablePan(mAdapter);
+ mTestUtils.unpair(mAdapter, device);
+ mTestUtils.acceptPair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
BluetoothTestRunner.sDevicePairPin);
for (int i = 0; i < iterations; i++) {
mTestUtils.writeOutput("incomingPanConnection iteration " + (i + 1) + " of "
+ iterations);
- mTestUtils.incomingPanConnection(adapter, device);
- mTestUtils.incomingPanDisconnection(adapter, device);
+ mTestUtils.incomingPanConnection(mAdapter, device);
+ mTestUtils.incomingPanDisconnection(mAdapter, device);
}
- mTestUtils.unpair(adapter, device);
- mTestUtils.disablePan(adapter);
- mTestUtils.disable(adapter);
+ mTestUtils.unpair(mAdapter, device);
+ mTestUtils.disablePan(mAdapter);
}
/**
@@ -368,28 +339,25 @@
return;
}
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
- mTestUtils.disable(adapter);
- mTestUtils.enable(adapter);
- mTestUtils.unpair(adapter, device);
- mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey,
+ BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
+ mTestUtils.enable(mAdapter);
+ mTestUtils.unpair(mAdapter, device);
+ mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
BluetoothTestRunner.sDevicePairPin);
- mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET, null);
- mTestUtils.connectProfile(adapter, device, BluetoothProfile.HEADSET, null);
- mTestUtils.stopSco(adapter, device);
+ mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HEADSET, null);
+ mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.HEADSET, null);
+ mTestUtils.stopSco(mAdapter, device);
for (int i = 0; i < iterations; i++) {
mTestUtils.writeOutput("startStopSco iteration " + (i + 1) + " of " + iterations);
- mTestUtils.startSco(adapter, device);
+ mTestUtils.startSco(mAdapter, device);
sleep(SCO_SLEEP_TIME);
- mTestUtils.stopSco(adapter, device);
+ mTestUtils.stopSco(mAdapter, device);
sleep(SCO_SLEEP_TIME);
}
- mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET, null);
- mTestUtils.unpair(adapter, device);
- mTestUtils.disable(adapter);
+ mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HEADSET, null);
+ mTestUtils.unpair(mAdapter, device);
}
private void sleep(long time) {
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
index 0d9980a..ee15978 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
@@ -35,6 +35,8 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
public class BluetoothTestUtils extends Assert {
@@ -423,57 +425,40 @@
* @param adapter The BT adapter.
*/
public void enable(BluetoothAdapter adapter) {
- int mask = (BluetoothReceiver.STATE_TURNING_ON_FLAG | BluetoothReceiver.STATE_ON_FLAG
- | BluetoothReceiver.SCAN_MODE_CONNECTABLE_FLAG);
- long start = -1;
- BluetoothReceiver receiver = getBluetoothReceiver(mask);
-
- int state = adapter.getState();
- switch (state) {
- case BluetoothAdapter.STATE_ON:
- assertTrue(adapter.isEnabled());
- removeReceiver(receiver);
- return;
- case BluetoothAdapter.STATE_TURNING_ON:
- assertFalse(adapter.isEnabled());
- mask = 0; // Don't check for received intents since we might have missed them.
- break;
- case BluetoothAdapter.STATE_OFF:
- assertFalse(adapter.isEnabled());
- start = System.currentTimeMillis();
- assertTrue(adapter.enable());
- break;
- case BluetoothAdapter.STATE_TURNING_OFF:
- start = System.currentTimeMillis();
- assertTrue(adapter.enable());
- break;
- default:
- removeReceiver(receiver);
- fail(String.format("enable() invalid state: state=%d", state));
- }
-
- long s = System.currentTimeMillis();
- while (System.currentTimeMillis() - s < ENABLE_DISABLE_TIMEOUT) {
- state = adapter.getState();
- if (state == BluetoothAdapter.STATE_ON
- && (receiver.getFiredFlags() & mask) == mask) {
- assertTrue(adapter.isEnabled());
- long finish = receiver.getCompletedTime();
- if (start != -1 && finish != -1) {
- writeOutput(String.format("enable() completed in %d ms", (finish - start)));
- } else {
- writeOutput("enable() completed");
+ writeOutput("Enabling Bluetooth adapter.");
+ assertFalse(adapter.isEnabled());
+ int btState = adapter.getState();
+ final Semaphore completionSemaphore = new Semaphore(0);
+ final BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (!BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
+ return;
}
- removeReceiver(receiver);
- return;
+ final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+ BluetoothAdapter.ERROR);
+ if (state == BluetoothAdapter.STATE_ON) {
+ completionSemaphore.release();
+ }
}
- sleep(POLL_TIME);
- }
+ };
- int firedFlags = receiver.getFiredFlags();
- removeReceiver(receiver);
- fail(String.format("enable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
- state, BluetoothAdapter.STATE_ON, firedFlags, mask));
+ final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
+ mContext.registerReceiver(receiver, filter);
+ assertTrue(adapter.enable());
+ boolean success = false;
+ try {
+ success = completionSemaphore.tryAcquire(ENABLE_DISABLE_TIMEOUT, TimeUnit.MILLISECONDS);
+ writeOutput(String.format("enable() completed in 0 ms"));
+ } catch (final InterruptedException e) {
+ // This should never happen but just in case it does, the test will fail anyway.
+ }
+ mContext.unregisterReceiver(receiver);
+ if (!success) {
+ fail(String.format("enable() timeout: state=%d (expected %d)", btState,
+ BluetoothAdapter.STATE_ON));
+ }
}
/**
@@ -483,57 +468,40 @@
* @param adapter The BT adapter.
*/
public void disable(BluetoothAdapter adapter) {
- int mask = (BluetoothReceiver.STATE_TURNING_OFF_FLAG | BluetoothReceiver.STATE_OFF_FLAG
- | BluetoothReceiver.SCAN_MODE_NONE_FLAG);
- long start = -1;
- BluetoothReceiver receiver = getBluetoothReceiver(mask);
-
- int state = adapter.getState();
- switch (state) {
- case BluetoothAdapter.STATE_OFF:
- assertFalse(adapter.isEnabled());
- removeReceiver(receiver);
- return;
- case BluetoothAdapter.STATE_TURNING_ON:
- assertFalse(adapter.isEnabled());
- start = System.currentTimeMillis();
- break;
- case BluetoothAdapter.STATE_ON:
- assertTrue(adapter.isEnabled());
- start = System.currentTimeMillis();
- assertTrue(adapter.disable());
- break;
- case BluetoothAdapter.STATE_TURNING_OFF:
- assertFalse(adapter.isEnabled());
- mask = 0; // Don't check for received intents since we might have missed them.
- break;
- default:
- removeReceiver(receiver);
- fail(String.format("disable() invalid state: state=%d", state));
- }
-
- long s = System.currentTimeMillis();
- while (System.currentTimeMillis() - s < ENABLE_DISABLE_TIMEOUT) {
- state = adapter.getState();
- if (state == BluetoothAdapter.STATE_OFF
- && (receiver.getFiredFlags() & mask) == mask) {
- assertFalse(adapter.isEnabled());
- long finish = receiver.getCompletedTime();
- if (start != -1 && finish != -1) {
- writeOutput(String.format("disable() completed in %d ms", (finish - start)));
- } else {
- writeOutput("disable() completed");
+ writeOutput("Disabling Bluetooth adapter.");
+ assertTrue(adapter.isEnabled());
+ int btState = adapter.getState();
+ final Semaphore completionSemaphore = new Semaphore(0);
+ final BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (!BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
+ return;
}
- removeReceiver(receiver);
- return;
+ final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+ BluetoothAdapter.ERROR);
+ if (state == BluetoothAdapter.STATE_OFF) {
+ completionSemaphore.release();
+ }
}
- sleep(POLL_TIME);
- }
+ };
- int firedFlags = receiver.getFiredFlags();
- removeReceiver(receiver);
- fail(String.format("disable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
- state, BluetoothAdapter.STATE_OFF, firedFlags, mask));
+ final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
+ mContext.registerReceiver(receiver, filter);
+ assertTrue(adapter.disable());
+ boolean success = false;
+ try {
+ success = completionSemaphore.tryAcquire(ENABLE_DISABLE_TIMEOUT, TimeUnit.MILLISECONDS);
+ writeOutput(String.format("disable() completed in 0 ms"));
+ } catch (final InterruptedException e) {
+ // This should never happen but just in case it does, the test will fail anyway.
+ }
+ mContext.unregisterReceiver(receiver);
+ if (!success) {
+ fail(String.format("disable() timeout: state=%d (expected %d)", btState,
+ BluetoothAdapter.STATE_OFF));
+ }
}
/**
@@ -543,40 +511,47 @@
* @param adapter The BT adapter.
*/
public void discoverable(BluetoothAdapter adapter) {
- int mask = BluetoothReceiver.SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
-
if (!adapter.isEnabled()) {
fail("discoverable() bluetooth not enabled");
}
int scanMode = adapter.getScanMode();
- if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
+ if (scanMode != BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
return;
}
- BluetoothReceiver receiver = getBluetoothReceiver(mask);
-
- assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE, scanMode);
- long start = System.currentTimeMillis();
- assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE));
-
- while (System.currentTimeMillis() - start < DISCOVERABLE_UNDISCOVERABLE_TIMEOUT) {
- scanMode = adapter.getScanMode();
- if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE
- && (receiver.getFiredFlags() & mask) == mask) {
- writeOutput(String.format("discoverable() completed in %d ms",
- (receiver.getCompletedTime() - start)));
- removeReceiver(receiver);
- return;
+ final Semaphore completionSemaphore = new Semaphore(0);
+ final BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (!BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) {
+ return;
+ }
+ final int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,
+ BluetoothAdapter.SCAN_MODE_NONE);
+ if (mode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
+ completionSemaphore.release();
+ }
}
- sleep(POLL_TIME);
- }
+ };
- int firedFlags = receiver.getFiredFlags();
- removeReceiver(receiver);
- fail(String.format("discoverable() timeout: scanMode=%d (expected %d), flags=0x%x "
- + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE,
- firedFlags, mask));
+ final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
+ mContext.registerReceiver(receiver, filter);
+ assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE));
+ boolean success = false;
+ try {
+ success = completionSemaphore.tryAcquire(DISCOVERABLE_UNDISCOVERABLE_TIMEOUT,
+ TimeUnit.MILLISECONDS);
+ writeOutput(String.format("discoverable() completed in 0 ms"));
+ } catch (final InterruptedException e) {
+ // This should never happen but just in case it does, the test will fail anyway.
+ }
+ mContext.unregisterReceiver(receiver);
+ if (!success) {
+ fail(String.format("discoverable() timeout: scanMode=%d (expected %d)", scanMode,
+ BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE));
+ }
}
/**
@@ -586,40 +561,47 @@
* @param adapter The BT adapter.
*/
public void undiscoverable(BluetoothAdapter adapter) {
- int mask = BluetoothReceiver.SCAN_MODE_CONNECTABLE_FLAG;
-
if (!adapter.isEnabled()) {
fail("undiscoverable() bluetooth not enabled");
}
int scanMode = adapter.getScanMode();
- if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
+ if (scanMode != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
return;
}
- BluetoothReceiver receiver = getBluetoothReceiver(mask);
-
- assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, scanMode);
- long start = System.currentTimeMillis();
- assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE));
-
- while (System.currentTimeMillis() - start < DISCOVERABLE_UNDISCOVERABLE_TIMEOUT) {
- scanMode = adapter.getScanMode();
- if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE
- && (receiver.getFiredFlags() & mask) == mask) {
- writeOutput(String.format("undiscoverable() completed in %d ms",
- (receiver.getCompletedTime() - start)));
- removeReceiver(receiver);
- return;
+ final Semaphore completionSemaphore = new Semaphore(0);
+ final BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (!BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) {
+ return;
+ }
+ final int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,
+ BluetoothAdapter.SCAN_MODE_NONE);
+ if (mode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
+ completionSemaphore.release();
+ }
}
- sleep(POLL_TIME);
- }
+ };
- int firedFlags = receiver.getFiredFlags();
- removeReceiver(receiver);
- fail(String.format("undiscoverable() timeout: scanMode=%d (expected %d), flags=0x%x "
- + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE, firedFlags,
- mask));
+ final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
+ mContext.registerReceiver(receiver, filter);
+ assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE));
+ boolean success = false;
+ try {
+ success = completionSemaphore.tryAcquire(DISCOVERABLE_UNDISCOVERABLE_TIMEOUT,
+ TimeUnit.MILLISECONDS);
+ writeOutput(String.format("undiscoverable() completed in 0 ms"));
+ } catch (InterruptedException e) {
+ // This should never happen but just in case it does, the test will fail anyway.
+ }
+ mContext.unregisterReceiver(receiver);
+ if (!success) {
+ fail(String.format("undiscoverable() timeout: scanMode=%d (expected %d)", scanMode,
+ BluetoothAdapter.SCAN_MODE_CONNECTABLE));
+ }
}
/**
diff --git a/core/tests/coretests/src/android/app/ApplicationErrorReportTest.java b/core/tests/coretests/src/android/app/ApplicationErrorReportTest.java
new file mode 100644
index 0000000..19a390a
--- /dev/null
+++ b/core/tests/coretests/src/android/app/ApplicationErrorReportTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.app;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.app.ApplicationErrorReport.CrashInfo;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ApplicationErrorReportTest {
+
+ @Test
+ public void testHugeStacktraceLeadsToReasonableReport() {
+ Throwable deepStackTrace = deepStackTrace();
+ CrashInfo crashInfo = new CrashInfo(deepStackTrace);
+
+ assertTrue("stack trace is longer than 50'000 characters",
+ crashInfo.stackTrace.length() < 50000);
+ }
+
+ @Test
+ public void testHugeExceptionMessageLeadsToReasonableReport() {
+ StringBuilder msg = new StringBuilder();
+ for (int i = 0; i < 1000000; i++) {
+ msg.append('x');
+ }
+
+ CrashInfo crashInfo = new CrashInfo(new Throwable(msg.toString()));
+
+ assertTrue("message is longer than 50'000 characters",
+ crashInfo.exceptionMessage.length() < 50000);
+ }
+
+ @Test
+ public void testTruncationKeepsStartAndEndIntact() {
+ StringBuilder msg = new StringBuilder("start");
+ for (int i = 0; i < 1000000; i++) {
+ msg.append('x');
+ }
+ msg.append("end");
+
+ CrashInfo crashInfo = new CrashInfo(new Throwable(msg.toString()));
+
+ String exceptionMessage = crashInfo.exceptionMessage;
+ assertEquals("start", exceptionMessage.substring(0, "start".length()));
+ assertEquals("end", exceptionMessage.substring(exceptionMessage.length() - "end".length()));
+ }
+
+ /**
+ * @return a Throwable with a very long stack trace.
+ */
+ private Throwable deepStackTrace() {
+ return stackTraceGenerator__aaaaaaaaa_aaaaaaaaa_aaaaaaaaa_aaaaaaaaa_aaaaaaaaa(1000);
+ }
+
+ private Throwable stackTraceGenerator__aaaaaaaaa_aaaaaaaaa_aaaaaaaaa_aaaaaaaaa_aaaaaaaaa(
+ int d) {
+ if (d > 0) {
+ return stackTraceGenerator__aaaaaaaaa_aaaaaaaaa_aaaaaaaaa_aaaaaaaaa_aaaaaaaaa(d - 1);
+ } else {
+ return new Throwable("here");
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/text/method/BackspaceTest.java b/core/tests/coretests/src/android/text/method/BackspaceTest.java
index a9fa4dd..fd686b9 100644
--- a/core/tests/coretests/src/android/text/method/BackspaceTest.java
+++ b/core/tests/coretests/src/android/text/method/BackspaceTest.java
@@ -170,10 +170,27 @@
backspace(state, 0);
state.assertEquals("|");
+ state.setByString("U+1F469 U+200D U+1F373 |");
+ backspace(state, 0);
+ state.assertEquals("|");
+
+ state.setByString("U+1F487 U+200D U+2640 |");
+ backspace(state, 0);
+ state.assertEquals("|");
+
+ state.setByString("U+1F487 U+200D U+2640 U+FE0F |");
+ backspace(state, 0);
+ state.assertEquals("|");
+
state.setByString("U+1F468 U+200D U+2764 U+FE0F U+200D U+1F48B U+200D U+1F468 |");
backspace(state, 0);
state.assertEquals("|");
+ // Emoji modifier can be appended to the first emoji.
+ state.setByString("U+1F469 U+1F3FB U+200D U+1F4BC |");
+ backspace(state, 0);
+ state.assertEquals("|");
+
// End with ZERO WIDTH JOINER
state.setByString("U+1F441 U+200D |");
backspace(state, 0);
@@ -445,13 +462,6 @@
backspace(state, 0);
state.assertEquals("|");
- // Emoji modifier + ZERO WIDTH JOINER
- state.setByString("U+1F466 U+1F3FB U+200D U+1F469 |");
- backspace(state, 0);
- state.assertEquals("U+1F466 |");
- backspace(state, 0);
- state.assertEquals("|");
-
// Regional indicator symbol + Emoji modifier
state.setByString("U+1F1FA U+1F3FB |");
backspace(state, 0);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java
new file mode 100644
index 0000000..a15e367
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.internal.os;
+
+import android.os.BatteryStats;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.util.Log;
+
+import junit.framework.TestCase;
+
+import org.mockito.Mockito;
+
+/**
+ * Test BatteryStatsImpl.DurationTimer.
+ *
+ * In these tests, unless otherwise commented, the time increments by
+ * 2x + 100, to make the subtraction unlikely to alias to another time.
+ */
+public class BatteryStatsDurationTimerTest extends TestCase {
+
+ @SmallTest
+ public void testStartStop() throws Exception {
+ final MockClocks clocks = new MockClocks();
+
+ final BatteryStatsImpl.TimeBase timeBase = new BatteryStatsImpl.TimeBase();
+ timeBase.init(clocks.uptimeMillis(), clocks.elapsedRealtime());
+
+ final BatteryStatsImpl.DurationTimer timer = new BatteryStatsImpl.DurationTimer(clocks,
+ null, BatteryStats.WAKE_TYPE_PARTIAL, null, timeBase);
+
+ // TimeBase running, timer not running: current and max are 0
+ timeBase.setRunning(true, /* uptimeUs */ 0, /* realtimeUs */ 100*1000);
+ assertFalse(timer.isRunningLocked());
+ assertEquals(0, timer.getCurrentDurationMsLocked(300));
+ assertEquals(0, timer.getMaxDurationMsLocked(301));
+
+ // Start timer: current and max advance
+ timer.startRunningLocked(700);
+ assertTrue(timer.isRunningLocked());
+ assertEquals(800, timer.getCurrentDurationMsLocked(1500));
+ assertEquals(801, timer.getMaxDurationMsLocked(1501));
+
+ // Stop timer: current resets to 0, max remains
+ timer.stopRunningLocked(3100);
+ assertFalse(timer.isRunningLocked());
+ assertEquals(0, timer.getCurrentDurationMsLocked(6300));
+ assertEquals(2400, timer.getMaxDurationMsLocked(6301));
+
+ // Start time again, but check with a short time, and make sure max doesn't
+ // increment.
+ timer.startRunningLocked(12700);
+ assertTrue(timer.isRunningLocked());
+ assertEquals(100, timer.getCurrentDurationMsLocked(12800));
+ assertEquals(2400, timer.getMaxDurationMsLocked(12801));
+
+ // And stop it again, but with a short time, and make sure it doesn't increment.
+ timer.stopRunningLocked(12900);
+ assertFalse(timer.isRunningLocked());
+ assertEquals(0, timer.getCurrentDurationMsLocked(13000));
+ assertEquals(2400, timer.getMaxDurationMsLocked(13001));
+
+ // Now start and check that the time doesn't increase if the two times are the same.
+ timer.startRunningLocked(27000);
+ assertTrue(timer.isRunningLocked());
+ assertEquals(0, timer.getCurrentDurationMsLocked(27000));
+ assertEquals(2400, timer.getMaxDurationMsLocked(27000));
+
+ // Stop the TimeBase. The values should be frozen.
+ timeBase.setRunning(false, /* uptimeUs */ 10, /* realtimeUs */ 55000*1000);
+ assertTrue(timer.isRunningLocked());
+ assertEquals(28100, timer.getCurrentDurationMsLocked(110100)); // Why 28100 and not 28000?
+ assertEquals(28100, timer.getMaxDurationMsLocked(110101));
+
+ // Start the TimeBase. The values should be the old value plus the delta
+ // between when the timer restarted and the current time
+ timeBase.setRunning(true, /* uptimeUs */ 10, /* realtimeUs */ 220100*1000);
+ assertTrue(timer.isRunningLocked());
+ assertEquals(28300, timer.getCurrentDurationMsLocked(220300)); // extra 100 from above??
+ assertEquals(28301, timer.getMaxDurationMsLocked(220301));
+ }
+
+ @SmallTest
+ public void testReset() throws Exception {
+ }
+
+ @SmallTest
+ public void testParceling() throws Exception {
+ final MockClocks clocks = new MockClocks();
+
+ final BatteryStatsImpl.TimeBase timeBase = new BatteryStatsImpl.TimeBase();
+ timeBase.init(clocks.uptimeMillis(), clocks.elapsedRealtime());
+
+ final BatteryStatsImpl.DurationTimer timer = new BatteryStatsImpl.DurationTimer(clocks,
+ null, BatteryStats.WAKE_TYPE_PARTIAL, null, timeBase);
+
+ // Start running on battery.
+ clocks.realtime = 100;
+ clocks.uptime = 10;
+ timeBase.setRunning(true, clocks.uptimeMillis()*1000, clocks.elapsedRealtime()*1000);
+
+ timer.startRunningLocked(300);
+
+ // Check that it did start running
+ assertEquals(400, timer.getMaxDurationMsLocked(700));
+ assertEquals(401, timer.getCurrentDurationMsLocked(701));
+
+ // Write summary
+ final Parcel summaryParcel = Parcel.obtain();
+ timer.writeSummaryFromParcelLocked(summaryParcel, 1500*1000);
+ summaryParcel.setDataPosition(0);
+
+ // Read summary
+ final BatteryStatsImpl.DurationTimer summary = new BatteryStatsImpl.DurationTimer(clocks,
+ null, BatteryStats.WAKE_TYPE_PARTIAL, null, timeBase);
+ summary.startRunningLocked(3100);
+ summary.readSummaryFromParcelLocked(summaryParcel);
+ // The new one shouldn't be running, and therefore 0 for current time
+ assertFalse(summary.isRunningLocked());
+ assertEquals(0, summary.getCurrentDurationMsLocked(6300));
+ // The new one should have the max duration that we had when we wrote it
+ assertEquals(1200, summary.getMaxDurationMsLocked(6301));
+
+ // Write full
+ final Parcel fullParcel = Parcel.obtain();
+ timer.writeToParcel(fullParcel, 1500*1000);
+ fullParcel.setDataPosition(0);
+
+ // Read full - Should be the same as the summary as far as DurationTimer is concerned.
+ final BatteryStatsImpl.DurationTimer full = new BatteryStatsImpl.DurationTimer(clocks,
+ null, BatteryStats.WAKE_TYPE_PARTIAL, null, timeBase, fullParcel);
+ // The new one shouldn't be running, and therefore 0 for current time
+ assertFalse(full.isRunningLocked());
+ assertEquals(0, full.getCurrentDurationMsLocked(6300));
+ // The new one should have the max duration that we had when we wrote it
+ assertEquals(1200, full.getMaxDurationMsLocked(6301));
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
index ce6879d..b4afdda 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
@@ -178,19 +178,40 @@
clocks.elapsedRealtime() * 1000);
offBatterySummaryParcel.setDataPosition(0);
- // Read the on battery summary from the parcel.
- BatteryStatsImpl.SamplingTimer unparceledTimer = new BatteryStatsImpl.SamplingTimer(
- clocks, timeBase);
- unparceledTimer.readSummaryFromParcelLocked(onBatterySummaryParcel);
+ // Set the timebase running again. That way any issues with tracking reported values
+ // get tested when we unparcel the timers below.
+ timeBase.setRunning(true, clocks.uptimeMillis(), clocks.elapsedRealtime());
- assertEquals(10, unparceledTimer.getTotalTimeLocked(0, BatteryStats.STATS_SINCE_CHARGED));
- assertEquals(1, unparceledTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
+ // Read the on battery summary from the parcel.
+ BatteryStatsImpl.SamplingTimer unparceledOnBatteryTimer =
+ new BatteryStatsImpl.SamplingTimer(clocks, timeBase);
+ unparceledOnBatteryTimer.readSummaryFromParcelLocked(onBatterySummaryParcel);
+
+ assertEquals(10, unparceledOnBatteryTimer.getTotalTimeLocked(0,
+ BatteryStats.STATS_SINCE_CHARGED));
+ assertEquals(1, unparceledOnBatteryTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
// Read the off battery summary from the parcel.
- unparceledTimer = new BatteryStatsImpl.SamplingTimer(clocks, timeBase);
- unparceledTimer.readSummaryFromParcelLocked(offBatterySummaryParcel);
+ BatteryStatsImpl.SamplingTimer unparceledOffBatteryTimer =
+ new BatteryStatsImpl.SamplingTimer(clocks, timeBase);
+ unparceledOffBatteryTimer.readSummaryFromParcelLocked(offBatterySummaryParcel);
- assertEquals(10, unparceledTimer.getTotalTimeLocked(0, BatteryStats.STATS_SINCE_CHARGED));
- assertEquals(1, unparceledTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
+ assertEquals(10, unparceledOffBatteryTimer.getTotalTimeLocked(0,
+ BatteryStats.STATS_SINCE_CHARGED));
+ assertEquals(1, unparceledOffBatteryTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
+
+ // Now, just like with a fresh timer, the first update should be absorbed to account for
+ // data being collected when we weren't recording.
+ unparceledOnBatteryTimer.update(10, 10);
+
+ assertEquals(10, unparceledOnBatteryTimer.getTotalTimeLocked(0,
+ BatteryStats.STATS_SINCE_CHARGED));
+ assertEquals(1, unparceledOnBatteryTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
+
+ unparceledOffBatteryTimer.update(10, 10);
+
+ assertEquals(10, unparceledOffBatteryTimer.getTotalTimeLocked(0,
+ BatteryStats.STATS_SINCE_CHARGED));
+ assertEquals(1, unparceledOffBatteryTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 78bcbbc..9518219 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -5,6 +5,7 @@
@RunWith(Suite.class)
@Suite.SuiteClasses({
+ BatteryStatsDurationTimerTest.class,
BatteryStatsSamplingTimerTest.class,
BatteryStatsServTest.class,
BatteryStatsTimeBaseTest.class,
diff --git a/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
index da8bc1d..7935880 100644
--- a/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
@@ -45,6 +45,7 @@
private static final int TEST_CMD = 18;
private static final int TEST_ARG1 = 33;
private static final int TEST_ARG2 = 182;
+ private static final Object TEST_OBJ = "hello";
@Mock AlarmManager mAlarmManager;
WakeupMessage mMessage;
@@ -92,7 +93,7 @@
mListenerCaptor.capture(), any(Handler.class));
mMessage = new WakeupMessage(context, mHandler, TEST_CMD_NAME, TEST_CMD, TEST_ARG1,
- TEST_ARG2);
+ TEST_ARG2, TEST_OBJ);
}
/**
@@ -114,6 +115,7 @@
assertEquals("what", TEST_CMD, mHandler.getLastMessage().what);
assertEquals("arg1", TEST_ARG1, mHandler.getLastMessage().arg1);
assertEquals("arg2", TEST_ARG2, mHandler.getLastMessage().arg2);
+ assertEquals("obj", TEST_OBJ, mHandler.getLastMessage().obj);
}
/**
diff --git a/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg
index 6af4d70..3d1f8ea 100755
--- a/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg
+++ b/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg
Binary files differ
diff --git a/docs/html/guide/topics/manifest/uses-feature-element.jd b/docs/html/guide/topics/manifest/uses-feature-element.jd
index 10841d6..9b32244 100755
--- a/docs/html/guide/topics/manifest/uses-feature-element.jd
+++ b/docs/html/guide/topics/manifest/uses-feature-element.jd
@@ -1230,7 +1230,7 @@
</p>
<p>
- When declared as required, this feature indicates that the app is
+ By default, your app requires this feature. This feature indicates that the app is
compatible with a device only if that device emulates a touchscreen
("fake touch" interface) or has an actual touchscreen.
</p>
@@ -1240,19 +1240,12 @@
that emulates a subset of a touchscreen's capabilities. For example, a
mouse or remote control could drive an on-screen cursor. If your app
requires basic point and click interaction (in other words, it won't work
- with only a d-pad controller), you should declare this feature. Because
+ with only a d-pad controller), you should declare this feature or simply
+ avoid declaring any {@code android.hardware.touchscreen.*} features. Because
this is the minimum level of touch interaction, you can also use an app
that declares this feature on devices that offer more complex touch
interfaces.
</p>
-
- <p class="note">
- <strong>Note:</strong> Apps require the {@code android.hardware.touchscreen}
- feature by default. If you want your app to be available to devices that
- provide a fake touch interface, you must also explicitly declare that a
- touchscreen is not required as follows:
- </p>
- <pre><uses-feature android:name="android.hardware.touchscreen" <strong>android:required="false"</strong> /></pre>
</dd>
<dt>
@@ -1327,21 +1320,9 @@
</p>
<p>
- By default, your app requires this feature. As such, your app is not
- available to devices that provide only an emulated touch interface ("fake
- touch") by default. If you want to make your app available on devices
- that provide a fake touch interface (or even on devices that provide only
- a d-pad controller), you must explicitly declare that a touchscreen is
- not required by declaring {@code android.hardware.touchscreen} with
- {@code android:required="false"}. You should add this declaration if your
- app uses—but does not require—a real touchscreen interface.
- </p>
-
- <p>
If your app in fact requires a touch interface (to perform more advanced
- touch gestures such as fling), then you don't need to declare any touch
- interface features because they're required by default. However, it's
- best if you explicitly declare all features that your app uses.
+ touch gestures such as fling), then you must explicitly declare this feature
+ or any advanced touchscreen features.
</p>
<p>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 1fdc1f5..49721cf 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -1667,10 +1667,10 @@
* and therefore is harmless.
*/
public void prepareToDraw() {
- // TODO: Consider having this start an async upload?
- // With inPurgeable no-op'd there's currently no use for this
- // method, but it could have interesting future uses.
checkRecycled("Can't prepareToDraw on a recycled bitmap!");
+ // Kick off an update/upload of the bitmap outside of the normal
+ // draw path.
+ nativePrepareToDraw(mNativePtr);
}
/**
@@ -1741,4 +1741,5 @@
private static native void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap);
private static native boolean nativeSameAs(long nativeBitmap0, long nativeBitmap1);
private static native long nativeRefPixelRef(long nativeBitmap);
+ private static native void nativePrepareToDraw(long nativeBitmap);
}
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index cb6c92e..40b877d 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -788,7 +788,7 @@
* @return true if the resulting is non-empty
*/
public boolean clipPath(@NonNull Path path, @NonNull Region.Op op) {
- return native_clipPath(mNativeCanvasWrapper, path.ni(), op.nativeInt);
+ return native_clipPath(mNativeCanvasWrapper, path.readOnlyNI(), op.nativeInt);
}
/**
@@ -907,7 +907,7 @@
* does not intersect with the canvas' clip
*/
public boolean quickReject(@NonNull Path path, @NonNull EdgeType type) {
- return native_quickReject(mNativeCanvasWrapper, path.ni());
+ return native_quickReject(mNativeCanvasWrapper, path.readOnlyNI());
}
/**
@@ -1259,7 +1259,7 @@
if (path.isSimplePath && path.rects != null) {
native_drawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance());
} else {
- native_drawPath(mNativeCanvasWrapper, path.ni(), paint.getNativeInstance());
+ native_drawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance());
}
}
@@ -1895,7 +1895,7 @@
throw new ArrayIndexOutOfBoundsException();
}
native_drawTextOnPath(mNativeCanvasWrapper, text, index, count,
- path.ni(), hOffset, vOffset,
+ path.readOnlyNI(), hOffset, vOffset,
paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface);
}
@@ -1915,7 +1915,7 @@
public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,
float vOffset, @NonNull Paint paint) {
if (text.length() > 0) {
- native_drawTextOnPath(mNativeCanvasWrapper, text, path.ni(), hOffset, vOffset,
+ native_drawTextOnPath(mNativeCanvasWrapper, text, path.readOnlyNI(), hOffset, vOffset,
paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface);
}
}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 054e29f..cfbe672 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1021,7 +1021,7 @@
* drawn with a hairline (width == 0)
*/
public boolean getFillPath(Path src, Path dst) {
- return nGetFillPath(mNativePaint, src.ni(), dst.ni());
+ return nGetFillPath(mNativePaint, src.readOnlyNI(), dst.mutateNI());
}
/**
@@ -2394,7 +2394,7 @@
throw new ArrayIndexOutOfBoundsException();
}
nGetTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, index, count, x, y,
- path.ni());
+ path.mutateNI());
}
/**
@@ -2416,7 +2416,7 @@
throw new IndexOutOfBoundsException();
}
nGetTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, start, end, x, y,
- path.ni());
+ path.mutateNI());
}
/**
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index de391af..be31bbe 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -775,7 +775,12 @@
}
}
- final long ni() {
+ final long readOnlyNI() {
+ return mNativePath;
+ }
+
+ final long mutateNI() {
+ isSimplePath = false;
return mNativePath;
}
diff --git a/graphics/java/android/graphics/PathDashPathEffect.java b/graphics/java/android/graphics/PathDashPathEffect.java
index 4f43f68..2b6a6ed 100644
--- a/graphics/java/android/graphics/PathDashPathEffect.java
+++ b/graphics/java/android/graphics/PathDashPathEffect.java
@@ -41,7 +41,7 @@
*/
public PathDashPathEffect(Path shape, float advance, float phase,
Style style) {
- native_instance = nativeCreate(shape.ni(), advance, phase,
+ native_instance = nativeCreate(shape.readOnlyNI(), advance, phase,
style.native_style);
}
diff --git a/graphics/java/android/graphics/PathMeasure.java b/graphics/java/android/graphics/PathMeasure.java
index 2848949..78d892e 100644
--- a/graphics/java/android/graphics/PathMeasure.java
+++ b/graphics/java/android/graphics/PathMeasure.java
@@ -50,7 +50,7 @@
public PathMeasure(Path path, boolean forceClosed) {
// The native implementation does not copy the path, prevent it from being GC'd
mPath = path;
- native_instance = native_create(path != null ? path.ni() : 0,
+ native_instance = native_create(path != null ? path.readOnlyNI() : 0,
forceClosed);
}
@@ -60,7 +60,7 @@
public void setPath(Path path, boolean forceClosed) {
mPath = path;
native_setPath(native_instance,
- path != null ? path.ni() : 0,
+ path != null ? path.readOnlyNI() : 0,
forceClosed);
}
@@ -134,8 +134,7 @@
return false;
}
- dst.isSimplePath = false;
- return native_getSegment(native_instance, startD, stopD, dst.ni(), startWithMoveTo);
+ return native_getSegment(native_instance, startD, stopD, dst.mutateNI(), startWithMoveTo);
}
/**
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index 28d8690..08eeaff 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -21,11 +21,14 @@
/**
* A Picture records drawing calls (via the canvas returned by beginRecording)
- * and can then play them back into Canvas (via {@link Picture#draw(Canvas)} or
+ * and can then play them back into Canvas (via {@link Picture#draw(Canvas)} or
* {@link Canvas#drawPicture(Picture)}).For most content (e.g. text, lines, rectangles),
* drawing a sequence from a picture can be faster than the equivalent API
* calls, since the picture performs its playback without incurring any
* method-call overhead.
+ *
+ * <p class="note"><strong>Note:</strong> Prior to API level 23 a picture cannot
+ * be replayed on a hardware accelerated canvas.</p>
*/
public class Picture {
private Canvas mRecordingCanvas;
@@ -135,10 +138,6 @@
* have been persisted across device restarts are not guaranteed to decode
* properly and are highly discouraged.
*
- * <p>
- * <strong>Note:</strong> Prior to API level 23 a picture created from an
- * input stream cannot be replayed on a hardware accelerated canvas.
- *
* @see #writeToStream(java.io.OutputStream)
* @deprecated The recommended alternative is to not use writeToStream and
* instead draw the picture into a Bitmap from which you can persist it as
@@ -155,10 +154,6 @@
* The resulting stream is NOT to be persisted across device restarts as
* there is no guarantee that the Picture can be successfully reconstructed.
*
- * <p>
- * <strong>Note:</strong> Prior to API level 23 a picture created from an
- * input stream cannot be replayed on a hardware accelerated canvas.
- *
* @see #createFromStream(java.io.InputStream)
* @deprecated The recommended alternative is to draw the picture into a
* Bitmap from which you can persist it as raw or compressed pixels.
diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java
index de89ad0..dca6d9e 100644
--- a/graphics/java/android/graphics/Region.java
+++ b/graphics/java/android/graphics/Region.java
@@ -110,7 +110,7 @@
* (with no antialiasing).
*/
public boolean setPath(Path path, Region clip) {
- return nativeSetPath(mNativeRegion, path.ni(), clip.mNativeRegion);
+ return nativeSetPath(mNativeRegion, path.readOnlyNI(), clip.mNativeRegion);
}
/**
@@ -155,7 +155,7 @@
*/
public Path getBoundaryPath() {
Path path = new Path();
- nativeGetBoundaryPath(mNativeRegion, path.ni());
+ nativeGetBoundaryPath(mNativeRegion, path.mutateNI());
return path;
}
@@ -164,7 +164,7 @@
* path will also be empty.
*/
public boolean getBoundaryPath(Path path) {
- return nativeGetBoundaryPath(mNativeRegion, path.ni());
+ return nativeGetBoundaryPath(mNativeRegion, path.mutateNI());
}
/**
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 6762bea..c836204 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -46,6 +46,7 @@
import android.util.Log;
import android.util.LongArray;
import android.util.PathParser;
+import android.util.Property;
import android.util.TimeUtils;
import android.view.Choreographer;
import android.view.DisplayListCanvas;
@@ -157,7 +158,7 @@
private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false;
/** Local, mutable animator set. */
- private VectorDrawableAnimator mAnimatorSet = new VectorDrawableAnimatorUI(this);
+ private VectorDrawableAnimator mAnimatorSet;
/**
* The resources against which this drawable was created. Used to attempt
@@ -182,6 +183,7 @@
private AnimatedVectorDrawable(AnimatedVectorDrawableState state, Resources res) {
mAnimatedVectorState = new AnimatedVectorDrawableState(state, mCallback, res);
+ mAnimatorSet = new VectorDrawableAnimatorRT(this);
mRes = res;
}
@@ -208,7 +210,7 @@
/**
* In order to avoid breaking old apps, we only throw exception on invalid VectorDrawable
- * animations * for apps targeting N and later. For older apps, we ignore (i.e. quietly skip)
+ * animations for apps targeting N and later. For older apps, we ignore (i.e. quietly skip)
* these animations.
*
* @return whether invalid animations for vector drawable should be ignored.
@@ -237,6 +239,14 @@
@Override
public void draw(Canvas canvas) {
+ if (!canvas.isHardwareAccelerated() && mAnimatorSet instanceof VectorDrawableAnimatorRT) {
+ // If we have SW canvas and the RT animation is waiting to start, We need to fallback
+ // to UI thread animation for AVD.
+ if (!mAnimatorSet.isRunning() &&
+ ((VectorDrawableAnimatorRT) mAnimatorSet).mPendingAnimationActions.size() > 0) {
+ fallbackOntoUI();
+ }
+ }
mAnimatorSet.onDraw(canvas);
mAnimatedVectorState.mVectorDrawable.draw(canvas);
}
@@ -390,9 +400,12 @@
R.styleable.AnimatedVectorDrawableTarget_animation, 0);
if (animResId != 0) {
if (theme != null) {
- final Animator objectAnimator = AnimatorInflater.loadAnimator(
+ // The animator here could be ObjectAnimator or AnimatorSet.
+ final Animator animator = AnimatorInflater.loadAnimator(
res, theme, animResId, pathErrorScale);
- state.addTargetAnimator(target, objectAnimator);
+ updateAnimatorProperty(animator, target, state.mVectorDrawable,
+ state.mShouldIgnoreInvalidAnim);
+ state.addTargetAnimator(target, animator);
} else {
// The animation may be theme-dependent. As a
// workaround until Animator has full support for
@@ -414,6 +427,55 @@
mRes = state.mPendingAnims == null ? null : res;
}
+ private static void updateAnimatorProperty(Animator animator, String targetName,
+ VectorDrawable vectorDrawable, boolean ignoreInvalidAnim) {
+ if (animator instanceof ObjectAnimator) {
+ // Change the property of the Animator from using reflection based on the property
+ // name to a Property object that wraps the setter and getter for modifying that
+ // specific property for a given object. By replacing the reflection with a direct call,
+ // we can largely reduce the time it takes for a animator to modify a VD property.
+ PropertyValuesHolder[] holders = ((ObjectAnimator) animator).getValues();
+ for (int i = 0; i < holders.length; i++) {
+ PropertyValuesHolder pvh = holders[i];
+ String propertyName = pvh.getPropertyName();
+ Object targetNameObj = vectorDrawable.getTargetByName(targetName);
+ Property property = null;
+ if (targetNameObj instanceof VectorDrawable.VObject) {
+ property = ((VectorDrawable.VObject) targetNameObj).getProperty(propertyName);
+ } else if (targetNameObj instanceof VectorDrawable.VectorDrawableState) {
+ property = ((VectorDrawable.VectorDrawableState) targetNameObj)
+ .getProperty(propertyName);
+ }
+ if (property != null) {
+ if (containsSameValueType(pvh, property)) {
+ pvh.setProperty(property);
+ } else if (!ignoreInvalidAnim) {
+ throw new RuntimeException("Wrong valueType for Property: " + propertyName
+ + ". Expected type: " + property.getType().toString() + ". Actual "
+ + "type defined in resources: " + pvh.getValueType().toString());
+
+ }
+ }
+ }
+ } else if (animator instanceof AnimatorSet) {
+ for (Animator anim : ((AnimatorSet) animator).getChildAnimations()) {
+ updateAnimatorProperty(anim, targetName, vectorDrawable, ignoreInvalidAnim);
+ }
+ }
+ }
+
+ private static boolean containsSameValueType(PropertyValuesHolder holder, Property property) {
+ Class type1 = holder.getValueType();
+ Class type2 = property.getType();
+ if (type1 == float.class || type1 == Float.class) {
+ return type2 == float.class || type2 == Float.class;
+ } else if (type1 == int.class || type1 == Integer.class) {
+ return type2 == int.class || type2 == Integer.class;
+ } else {
+ return type1 == type2;
+ }
+ }
+
/**
* Force to animate on UI thread.
* @hide
@@ -425,10 +487,22 @@
throw new UnsupportedOperationException("Cannot force Animated Vector Drawable to" +
" run on UI thread when the animation has started on RenderThread.");
}
+ fallbackOntoUI();
+ }
+ }
+
+ private void fallbackOntoUI() {
+ if (mAnimatorSet instanceof VectorDrawableAnimatorRT) {
+ VectorDrawableAnimatorRT oldAnim = (VectorDrawableAnimatorRT) mAnimatorSet;
mAnimatorSet = new VectorDrawableAnimatorUI(this);
if (mAnimatorSetFromXml != null) {
mAnimatorSet.init(mAnimatorSetFromXml);
}
+ // Transfer the listener from RT animator to UI animator
+ if (oldAnim.mListener != null) {
+ mAnimatorSet.setListener(oldAnim.mListener);
+ }
+ oldAnim.transferPendingActions(mAnimatorSet);
}
}
@@ -462,6 +536,8 @@
@Config int mChangingConfigurations;
VectorDrawable mVectorDrawable;
+ private final boolean mShouldIgnoreInvalidAnim;
+
/** Animators that require a theme before inflation. */
ArrayList<PendingAnimator> mPendingAnims;
@@ -473,6 +549,7 @@
public AnimatedVectorDrawableState(AnimatedVectorDrawableState copy,
Callback owner, Resources res) {
+ mShouldIgnoreInvalidAnim = shouldIgnoreInvalidAnimation();
if (copy != null) {
mChangingConfigurations = copy.mChangingConfigurations;
@@ -616,8 +693,10 @@
for (int i = 0, count = pendingAnims.size(); i < count; i++) {
final PendingAnimator pendingAnimator = pendingAnims.get(i);
- final Animator objectAnimator = pendingAnimator.newInstance(res, t);
- addTargetAnimator(pendingAnimator.target, objectAnimator);
+ final Animator animator = pendingAnimator.newInstance(res, t);
+ updateAnimatorProperty(animator, pendingAnimator.target, mVectorDrawable,
+ mShouldIgnoreInvalidAnim);
+ addTargetAnimator(pendingAnimator.target, animator);
}
}
}
@@ -982,6 +1061,9 @@
private static final int REVERSE_ANIMATION = 2;
private static final int RESET_ANIMATION = 3;
private static final int END_ANIMATION = 4;
+
+ // If the duration of an animation is more than 300 frames, we cap the sample size to 300.
+ private static final int MAX_SAMPLE_POINTS = 300;
private AnimatorListener mListener = null;
private final LongArray mStartDelays = new LongArray();
private PropertyValuesHolder.PropertyValues mTmpValues =
@@ -992,14 +1074,12 @@
private boolean mInitialized = false;
private boolean mIsReversible = false;
private boolean mIsInfinite = false;
- // This needs to be set before parsing starts.
- private boolean mShouldIgnoreInvalidAnim;
// TODO: Consider using NativeAllocationRegistery to track native allocation
private final VirtualRefBasePtr mSetRefBasePtr;
private WeakReference<RenderNode> mLastSeenTarget = null;
private int mLastListenerId = 0;
private final IntArray mPendingAnimationActions = new IntArray();
- private final Drawable mDrawable;
+ private final AnimatedVectorDrawable mDrawable;
VectorDrawableAnimatorRT(AnimatedVectorDrawable drawable) {
mDrawable = drawable;
@@ -1016,8 +1096,10 @@
throw new UnsupportedOperationException("VectorDrawableAnimator cannot be " +
"re-initialized");
}
- mShouldIgnoreInvalidAnim = shouldIgnoreInvalidAnimation();
parseAnimatorSet(set, 0);
+ long vectorDrawableTreePtr = mDrawable.mAnimatedVectorState.mVectorDrawable
+ .getNativeTree();
+ nSetVectorDrawableTarget(mSetPtr, vectorDrawableTreePtr);
mInitialized = true;
mIsInfinite = set.getTotalDuration() == Animator.DURATION_INFINITE;
@@ -1077,7 +1159,7 @@
} else if (target instanceof VectorDrawable.VFullPath) {
createRTAnimatorForFullPath(animator, (VectorDrawable.VFullPath) target,
startTime);
- } else if (!mShouldIgnoreInvalidAnim) {
+ } else if (!mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
throw new IllegalArgumentException("ClipPath only supports PathData " +
"property");
}
@@ -1086,7 +1168,7 @@
} else if (target instanceof VectorDrawable.VectorDrawableState) {
createRTAnimatorForRootGroup(values, animator,
(VectorDrawable.VectorDrawableState) target, startTime);
- } else if (!mShouldIgnoreInvalidAnim) {
+ } else if (!mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
// Should never get here
throw new UnsupportedOperationException("Target should be either VGroup, VPath, " +
"or ConstantState, " + target == null ? "Null target" : target.getClass() +
@@ -1121,8 +1203,8 @@
long propertyPtr = nCreateGroupPropertyHolder(nativePtr, propertyId,
(Float) mTmpValues.startValue, (Float) mTmpValues.endValue);
if (mTmpValues.dataSource != null) {
- float[] dataPoints = createDataPoints(mTmpValues.dataSource, animator
- .getDuration());
+ float[] dataPoints = createFloatDataPoints(mTmpValues.dataSource,
+ animator.getDuration());
nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
}
createNativeChildAnimator(propertyPtr, startTime, animator);
@@ -1149,7 +1231,7 @@
long nativePtr = target.getNativePtr();
if (mTmpValues.type == Float.class || mTmpValues.type == float.class) {
if (propertyId < 0) {
- if (mShouldIgnoreInvalidAnim) {
+ if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
return;
} else {
throw new IllegalArgumentException("Property: " + mTmpValues.propertyName
@@ -1158,12 +1240,24 @@
}
propertyPtr = nCreatePathPropertyHolder(nativePtr, propertyId,
(Float) mTmpValues.startValue, (Float) mTmpValues.endValue);
+ if (mTmpValues.dataSource != null) {
+ // Pass keyframe data to native, if any.
+ float[] dataPoints = createFloatDataPoints(mTmpValues.dataSource,
+ animator.getDuration());
+ nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
+ }
} else if (mTmpValues.type == Integer.class || mTmpValues.type == int.class) {
propertyPtr = nCreatePathColorPropertyHolder(nativePtr, propertyId,
(Integer) mTmpValues.startValue, (Integer) mTmpValues.endValue);
+ if (mTmpValues.dataSource != null) {
+ // Pass keyframe data to native, if any.
+ int[] dataPoints = createIntDataPoints(mTmpValues.dataSource,
+ animator.getDuration());
+ nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
+ }
} else {
- if (mShouldIgnoreInvalidAnim) {
+ if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
return;
} else {
throw new UnsupportedOperationException("Unsupported type: " +
@@ -1171,45 +1265,63 @@
"supported for Paths.");
}
}
- if (mTmpValues.dataSource != null) {
- float[] dataPoints = createDataPoints(mTmpValues.dataSource, animator
- .getDuration());
- nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
- }
createNativeChildAnimator(propertyPtr, startTime, animator);
}
private void createRTAnimatorForRootGroup(PropertyValuesHolder[] values,
ObjectAnimator animator, VectorDrawable.VectorDrawableState target,
long startTime) {
- long nativePtr = target.getNativeRenderer();
- if (!animator.getPropertyName().equals("alpha")) {
- if (mShouldIgnoreInvalidAnim) {
- return;
- } else {
- throw new UnsupportedOperationException("Only alpha is supported for root "
- + "group");
- }
+ long nativePtr = target.getNativeRenderer();
+ if (!animator.getPropertyName().equals("alpha")) {
+ if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
+ return;
+ } else {
+ throw new UnsupportedOperationException("Only alpha is supported for root "
+ + "group");
}
- Float startValue = null;
- Float endValue = null;
- for (int i = 0; i < values.length; i++) {
- values[i].getPropertyValues(mTmpValues);
- if (mTmpValues.propertyName.equals("alpha")) {
- startValue = (Float) mTmpValues.startValue;
- endValue = (Float) mTmpValues.endValue;
- break;
- }
+ }
+ Float startValue = null;
+ Float endValue = null;
+ for (int i = 0; i < values.length; i++) {
+ values[i].getPropertyValues(mTmpValues);
+ if (mTmpValues.propertyName.equals("alpha")) {
+ startValue = (Float) mTmpValues.startValue;
+ endValue = (Float) mTmpValues.endValue;
+ break;
}
- if (startValue == null && endValue == null) {
- if (mShouldIgnoreInvalidAnim) {
- return;
- } else {
- throw new UnsupportedOperationException("No alpha values are specified");
- }
+ }
+ if (startValue == null && endValue == null) {
+ if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
+ return;
+ } else {
+ throw new UnsupportedOperationException("No alpha values are specified");
}
- long propertyPtr = nCreateRootAlphaPropertyHolder(nativePtr, startValue, endValue);
- createNativeChildAnimator(propertyPtr, startTime, animator);
+ }
+ long propertyPtr = nCreateRootAlphaPropertyHolder(nativePtr, startValue, endValue);
+ if (mTmpValues.dataSource != null) {
+ // Pass keyframe data to native, if any.
+ float[] dataPoints = createFloatDataPoints(mTmpValues.dataSource,
+ animator.getDuration());
+ nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
+ }
+ createNativeChildAnimator(propertyPtr, startTime, animator);
+ }
+
+ /**
+ * Calculate the amount of frames an animation will run based on duration.
+ */
+ private static int getFrameCount(long duration) {
+ long frameIntervalNanos = Choreographer.getInstance().getFrameIntervalNanos();
+ int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS);
+ int numAnimFrames = (int) Math.ceil(((double) duration) / animIntervalMs);
+ // We need 2 frames of data minimum.
+ numAnimFrames = Math.max(2, numAnimFrames);
+ if (numAnimFrames > MAX_SAMPLE_POINTS) {
+ Log.w("AnimatedVectorDrawable", "Duration for the animation is too long :" +
+ duration + ", the animation will subsample the keyframe or path data.");
+ numAnimFrames = MAX_SAMPLE_POINTS;
+ }
+ return numAnimFrames;
}
// These are the data points that define the value of the animating properties.
@@ -1217,11 +1329,9 @@
// a point on the path corresponds to the values of translateX and translateY.
// TODO: (Optimization) We should pass the path down in native and chop it into segments
// in native.
- private static float[] createDataPoints(
+ private static float[] createFloatDataPoints(
PropertyValuesHolder.PropertyValues.DataSource dataSource, long duration) {
- long frameIntervalNanos = Choreographer.getInstance().getFrameIntervalNanos();
- int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS);
- int numAnimFrames = (int) Math.ceil(((double) duration) / animIntervalMs);
+ int numAnimFrames = getFrameCount(duration);
float values[] = new float[numAnimFrames];
float lastFrame = numAnimFrames - 1;
for (int i = 0; i < numAnimFrames; i++) {
@@ -1231,6 +1341,18 @@
return values;
}
+ private static int[] createIntDataPoints(
+ PropertyValuesHolder.PropertyValues.DataSource dataSource, long duration) {
+ int numAnimFrames = getFrameCount(duration);
+ int values[] = new int[numAnimFrames];
+ float lastFrame = numAnimFrames - 1;
+ for (int i = 0; i < numAnimFrames; i++) {
+ float fraction = i / lastFrame;
+ values[i] = (Integer) dataSource.getValueAtFraction(fraction);
+ }
+ return values;
+ }
+
private void createNativeChildAnimator(long propertyPtr, long extraDelay,
ObjectAnimator animator) {
long duration = animator.getDuration();
@@ -1245,7 +1367,7 @@
mStartDelays.add(startDelay);
nAddAnimator(mSetPtr, propertyPtr, nativeInterpolator, startDelay, duration,
- repeatCount);
+ repeatCount, animator.getRepeatMode());
}
/**
@@ -1254,16 +1376,19 @@
* to the last seen RenderNode target and start right away.
*/
protected void recordLastSeenTarget(DisplayListCanvas canvas) {
- mLastSeenTarget = new WeakReference<RenderNode>(
- RenderNodeAnimatorSetHelper.getTarget(canvas));
- if (mPendingAnimationActions.size() > 0 && useLastSeenTarget()) {
- if (DBG_ANIMATION_VECTOR_DRAWABLE) {
- Log.d(LOGTAG, "Target is set in the next frame");
+ final RenderNode node = RenderNodeAnimatorSetHelper.getTarget(canvas);
+ mLastSeenTarget = new WeakReference<RenderNode>(node);
+ // Add the animator to the list of animators on every draw
+ if (mInitialized || mPendingAnimationActions.size() > 0) {
+ if (useTarget(node)) {
+ if (DBG_ANIMATION_VECTOR_DRAWABLE) {
+ Log.d(LOGTAG, "Target is set in the next frame");
+ }
+ for (int i = 0; i < mPendingAnimationActions.size(); i++) {
+ handlePendingAction(mPendingAnimationActions.get(i));
+ }
+ mPendingAnimationActions.clear();
}
- for (int i = 0; i < mPendingAnimationActions.size(); i++) {
- handlePendingAction(mPendingAnimationActions.get(i));
- }
- mPendingAnimationActions.clear();
}
}
@@ -1285,10 +1410,15 @@
private boolean useLastSeenTarget() {
if (mLastSeenTarget != null) {
final RenderNode target = mLastSeenTarget.get();
- if (target != null && target.isAttached()) {
- target.addAnimator(this);
- return true;
- }
+ return useTarget(target);
+ }
+ return false;
+ }
+
+ private boolean useTarget(RenderNode target) {
+ if (target != null && target.isAttached()) {
+ target.registerVectorDrawableAnimator(this);
+ return true;
}
return false;
}
@@ -1480,11 +1610,32 @@
private static void callOnFinished(VectorDrawableAnimatorRT set, int id) {
set.onAnimationEnd(id);
}
+
+ private void transferPendingActions(VectorDrawableAnimator animatorSet) {
+ for (int i = 0; i < mPendingAnimationActions.size(); i++) {
+ int pendingAction = mPendingAnimationActions.get(i);
+ if (pendingAction == START_ANIMATION) {
+ animatorSet.start();
+ } else if (pendingAction == END_ANIMATION) {
+ animatorSet.end();
+ } else if (pendingAction == REVERSE_ANIMATION) {
+ animatorSet.reverse();
+ } else if (pendingAction == RESET_ANIMATION) {
+ animatorSet.reset();
+ } else {
+ throw new UnsupportedOperationException("Animation action " +
+ pendingAction + "is not supported");
+ }
+ }
+ mPendingAnimationActions.clear();
+ }
}
private static native long nCreateAnimatorSet();
+ private static native void nSetVectorDrawableTarget(long animatorPtr, long vectorDrawablePtr);
private static native void nAddAnimator(long setPtr, long propertyValuesHolder,
- long nativeInterpolator, long startDelay, long duration, int repeatCount);
+ long nativeInterpolator, long startDelay, long duration, int repeatCount,
+ int repeatMode);
private static native long nCreateGroupPropertyHolder(long nativePtr, int propertyId,
float startValue, float endValue);
@@ -1498,6 +1649,7 @@
private static native long nCreateRootAlphaPropertyHolder(long nativePtr, float startValue,
float endValue);
private static native void nSetPropertyHolderData(long nativePtr, float[] data, int length);
+ private static native void nSetPropertyHolderData(long nativePtr, int[] data, int length);
private static native void nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
private static native void nReverse(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
private static native void nEnd(long animatorSetPtr);
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index bcc354c..3dbd2a9 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -699,7 +699,7 @@
float rad = mStrokePaint.getStrokeWidth();
canvas.saveLayer(mRect.left - rad, mRect.top - rad,
mRect.right + rad, mRect.bottom + rad,
- mLayerPaint, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
+ mLayerPaint);
// don't perform the filter in our individual paints
// since the layer will do it for us
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index c855c4c..dc1d18f 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -34,9 +34,12 @@
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
+import android.util.FloatProperty;
+import android.util.IntProperty;
import android.util.LayoutDirection;
import android.util.Log;
import android.util.PathParser;
+import android.util.Property;
import android.util.Xml;
import com.android.internal.R;
@@ -232,7 +235,7 @@
private final Rect mTmpBounds = new Rect();
public VectorDrawable() {
- this(new VectorDrawableState(), null);
+ this(new VectorDrawableState(null), null);
}
/**
@@ -763,6 +766,13 @@
return mVectorState.mAutoMirrored;
}
+ /**
+ * @hide
+ */
+ public long getNativeTree() {
+ return mVectorState.getNativeRenderer();
+ }
+
static class VectorDrawableState extends ConstantState {
// Variables below need to be copied (deep copy if applicable) for mutation.
int[] mThemeAttrs;
@@ -795,12 +805,33 @@
int mLastSWCachePixelCount = 0;
int mLastHWCachePixelCount = 0;
+ final static Property<VectorDrawableState, Float> ALPHA =
+ new FloatProperty<VectorDrawableState>("alpha") {
+ @Override
+ public void setValue(VectorDrawableState state, float value) {
+ state.setAlpha(value);
+ }
+
+ @Override
+ public Float get(VectorDrawableState state) {
+ return state.getAlpha();
+ }
+ };
+
+ Property getProperty(String propertyName) {
+ if (ALPHA.getName().equals(propertyName)) {
+ return ALPHA;
+ }
+ return null;
+ }
+
// This tracks the total native allocation for all the nodes.
private int mAllocationOfAllNodes = 0;
private static final int NATIVE_ALLOCATION_SIZE = 316;
- // Deep copy for mutate() or implicitly mutate.
+ // If copy is not null, deep copy the given VectorDrawableState. Otherwise, create a
+ // native vector drawable tree with an empty root group.
public VectorDrawableState(VectorDrawableState copy) {
if (copy != null) {
mThemeAttrs = copy.mThemeAttrs;
@@ -821,8 +852,11 @@
if (copy.mRootName != null) {
mVGTargetsMap.put(copy.mRootName, this);
}
- onTreeConstructionFinished();
+ } else {
+ mRootGroup = new VGroup();
+ createNativeTree(mRootGroup);
}
+ onTreeConstructionFinished();
}
private void createNativeTree(VGroup rootGroup) {
@@ -840,7 +874,8 @@
VMRuntime.getRuntime().registerNativeAllocation(NATIVE_ALLOCATION_SIZE);
}
-
+ // This should be called every time after a new RootGroup and all its subtrees are created
+ // (i.e. in constructors of VectorDrawableState and in inflate).
void onTreeConstructionFinished() {
mRootGroup.setTree(mNativeTree);
mAllocationOfAllNodes = mRootGroup.getNativeSize();
@@ -888,11 +923,6 @@
|| super.canApplyTheme();
}
- public VectorDrawableState() {
- mRootGroup = new VGroup();
- createNativeTree(mRootGroup);
- }
-
@Override
public Drawable newDrawable() {
return new VectorDrawable(this, null);
@@ -972,7 +1002,7 @@
}
static class VGroup extends VObject {
- private static final int ROTATE_INDEX = 0;
+ private static final int ROTATION_INDEX = 0;
private static final int PIVOT_X_INDEX = 1;
private static final int PIVOT_Y_INDEX = 2;
private static final int SCALE_X_INDEX = 3;
@@ -983,7 +1013,7 @@
private static final int NATIVE_ALLOCATION_SIZE = 100;
- private static final HashMap<String, Integer> sPropertyMap =
+ private static final HashMap<String, Integer> sPropertyIndexMap =
new HashMap<String, Integer>() {
{
put("translateX", TRANSLATE_X_INDEX);
@@ -992,19 +1022,123 @@
put("scaleY", SCALE_Y_INDEX);
put("pivotX", PIVOT_X_INDEX);
put("pivotY", PIVOT_Y_INDEX);
- put("rotation", ROTATE_INDEX);
+ put("rotation", ROTATION_INDEX);
}
};
static int getPropertyIndex(String propertyName) {
- if (sPropertyMap.containsKey(propertyName)) {
- return sPropertyMap.get(propertyName);
+ if (sPropertyIndexMap.containsKey(propertyName)) {
+ return sPropertyIndexMap.get(propertyName);
} else {
// property not found
return -1;
}
}
+ // Below are the Properties that wrap the setters to avoid reflection overhead in animations
+ private static final Property<VGroup, Float> TRANSLATE_X =
+ new FloatProperty<VGroup> ("translateX") {
+ @Override
+ public void setValue(VGroup object, float value) {
+ object.setTranslateX(value);
+ }
+
+ @Override
+ public Float get(VGroup object) {
+ return object.getTranslateX();
+ }
+ };
+
+ private static final Property<VGroup, Float> TRANSLATE_Y =
+ new FloatProperty<VGroup> ("translateY") {
+ @Override
+ public void setValue(VGroup object, float value) {
+ object.setTranslateY(value);
+ }
+
+ @Override
+ public Float get(VGroup object) {
+ return object.getTranslateY();
+ }
+ };
+
+ private static final Property<VGroup, Float> SCALE_X =
+ new FloatProperty<VGroup> ("scaleX") {
+ @Override
+ public void setValue(VGroup object, float value) {
+ object.setScaleX(value);
+ }
+
+ @Override
+ public Float get(VGroup object) {
+ return object.getScaleX();
+ }
+ };
+
+ private static final Property<VGroup, Float> SCALE_Y =
+ new FloatProperty<VGroup> ("scaleY") {
+ @Override
+ public void setValue(VGroup object, float value) {
+ object.setScaleY(value);
+ }
+
+ @Override
+ public Float get(VGroup object) {
+ return object.getScaleY();
+ }
+ };
+
+ private static final Property<VGroup, Float> PIVOT_X =
+ new FloatProperty<VGroup> ("pivotX") {
+ @Override
+ public void setValue(VGroup object, float value) {
+ object.setPivotX(value);
+ }
+
+ @Override
+ public Float get(VGroup object) {
+ return object.getPivotX();
+ }
+ };
+
+ private static final Property<VGroup, Float> PIVOT_Y =
+ new FloatProperty<VGroup> ("pivotY") {
+ @Override
+ public void setValue(VGroup object, float value) {
+ object.setPivotY(value);
+ }
+
+ @Override
+ public Float get(VGroup object) {
+ return object.getPivotY();
+ }
+ };
+
+ private static final Property<VGroup, Float> ROTATION =
+ new FloatProperty<VGroup> ("rotation") {
+ @Override
+ public void setValue(VGroup object, float value) {
+ object.setRotation(value);
+ }
+
+ @Override
+ public Float get(VGroup object) {
+ return object.getRotation();
+ }
+ };
+
+ private static final HashMap<String, Property> sPropertyMap =
+ new HashMap<String, Property>() {
+ {
+ put("translateX", TRANSLATE_X);
+ put("translateY", TRANSLATE_Y);
+ put("scaleX", SCALE_X);
+ put("scaleY", SCALE_Y);
+ put("pivotX", PIVOT_X);
+ put("pivotY", PIVOT_Y);
+ put("rotation", ROTATION);
+ }
+ };
// Temp array to store transform values obtained from native.
private float[] mTransform;
/////////////////////////////////////////////////////
@@ -1060,6 +1194,15 @@
mNativePtr = nCreateGroup();
}
+ Property getProperty(String propertyName) {
+ if (sPropertyMap.containsKey(propertyName)) {
+ return sPropertyMap.get(propertyName);
+ } else {
+ // property not found
+ return null;
+ }
+ }
+
public String getGroupName() {
return mGroupName;
}
@@ -1107,7 +1250,7 @@
throw new RuntimeException("Error: inconsistent property count");
}
float rotate = a.getFloat(R.styleable.VectorDrawableGroup_rotation,
- mTransform[ROTATE_INDEX]);
+ mTransform[ROTATION_INDEX]);
float pivotX = a.getFloat(R.styleable.VectorDrawableGroup_pivotX,
mTransform[PIVOT_X_INDEX]);
float pivotY = a.getFloat(R.styleable.VectorDrawableGroup_pivotY,
@@ -1293,6 +1436,27 @@
String mPathName;
@Config int mChangingConfigurations;
+ private static final Property<VPath, PathParser.PathData> PATH_DATA =
+ new Property<VPath, PathParser.PathData>(PathParser.PathData.class, "pathData") {
+ @Override
+ public void set(VPath object, PathParser.PathData data) {
+ object.setPathData(data);
+ }
+
+ @Override
+ public PathParser.PathData get(VPath object) {
+ return object.getPathData();
+ }
+ };
+
+ Property getProperty(String propertyName) {
+ if (PATH_DATA.getName().equals(propertyName)) {
+ return PATH_DATA;
+ }
+ // property not found
+ return null;
+ }
+
public VPath() {
// Empty constructor.
}
@@ -1415,7 +1579,7 @@
private static final int NATIVE_ALLOCATION_SIZE = 264;
// Property map for animatable attributes.
- private final static HashMap<String, Integer> sPropertyMap
+ private final static HashMap<String, Integer> sPropertyIndexMap
= new HashMap<String, Integer> () {
{
put("strokeWidth", STROKE_WIDTH_INDEX);
@@ -1429,6 +1593,125 @@
}
};
+ // Below are the Properties that wrap the setters to avoid reflection overhead in animations
+ private static final Property<VFullPath, Float> STROKE_WIDTH =
+ new FloatProperty<VFullPath> ("strokeWidth") {
+ @Override
+ public void setValue(VFullPath object, float value) {
+ object.setStrokeWidth(value);
+ }
+
+ @Override
+ public Float get(VFullPath object) {
+ return object.getStrokeWidth();
+ }
+ };
+
+ private static final Property<VFullPath, Integer> STROKE_COLOR =
+ new IntProperty<VFullPath> ("strokeColor") {
+ @Override
+ public void setValue(VFullPath object, int value) {
+ object.setStrokeColor(value);
+ }
+
+ @Override
+ public Integer get(VFullPath object) {
+ return object.getStrokeColor();
+ }
+ };
+
+ private static final Property<VFullPath, Float> STROKE_ALPHA =
+ new FloatProperty<VFullPath> ("strokeAlpha") {
+ @Override
+ public void setValue(VFullPath object, float value) {
+ object.setStrokeAlpha(value);
+ }
+
+ @Override
+ public Float get(VFullPath object) {
+ return object.getStrokeAlpha();
+ }
+ };
+
+ private static final Property<VFullPath, Integer> FILL_COLOR =
+ new IntProperty<VFullPath>("fillColor") {
+ @Override
+ public void setValue(VFullPath object, int value) {
+ object.setFillColor(value);
+ }
+
+ @Override
+ public Integer get(VFullPath object) {
+ return object.getFillColor();
+ }
+ };
+
+ private static final Property<VFullPath, Float> FILL_ALPHA =
+ new FloatProperty<VFullPath> ("fillAlpha") {
+ @Override
+ public void setValue(VFullPath object, float value) {
+ object.setFillAlpha(value);
+ }
+
+ @Override
+ public Float get(VFullPath object) {
+ return object.getFillAlpha();
+ }
+ };
+
+ private static final Property<VFullPath, Float> TRIM_PATH_START =
+ new FloatProperty<VFullPath> ("trimPathStart") {
+ @Override
+ public void setValue(VFullPath object, float value) {
+ object.setTrimPathStart(value);
+ }
+
+ @Override
+ public Float get(VFullPath object) {
+ return object.getTrimPathStart();
+ }
+ };
+
+ private static final Property<VFullPath, Float> TRIM_PATH_END =
+ new FloatProperty<VFullPath> ("trimPathEnd") {
+ @Override
+ public void setValue(VFullPath object, float value) {
+ object.setTrimPathEnd(value);
+ }
+
+ @Override
+ public Float get(VFullPath object) {
+ return object.getTrimPathEnd();
+ }
+ };
+
+ private static final Property<VFullPath, Float> TRIM_PATH_OFFSET =
+ new FloatProperty<VFullPath> ("trimPathOffset") {
+ @Override
+ public void setValue(VFullPath object, float value) {
+ object.setTrimPathOffset(value);
+ }
+
+ @Override
+ public Float get(VFullPath object) {
+ return object.getTrimPathOffset();
+ }
+ };
+
+ private final static HashMap<String, Property> sPropertyMap
+ = new HashMap<String, Property> () {
+ {
+ put("strokeWidth", STROKE_WIDTH);
+ put("strokeColor", STROKE_COLOR);
+ put("strokeAlpha", STROKE_ALPHA);
+ put("fillColor", FILL_COLOR);
+ put("fillAlpha", FILL_ALPHA);
+ put("trimPathStart", TRIM_PATH_START);
+ put("trimPathEnd", TRIM_PATH_END);
+ put("trimPathOffset", TRIM_PATH_OFFSET);
+ }
+ };
+
// Temp array to store property data obtained from native getter.
private byte[] mPropertyData;
/////////////////////////////////////////////////////
@@ -1451,11 +1734,24 @@
mFillColors = copy.mFillColors;
}
+ Property getProperty(String propertyName) {
+ Property p = super.getProperty(propertyName);
+ if (p != null) {
+ return p;
+ }
+ if (sPropertyMap.containsKey(propertyName)) {
+ return sPropertyMap.get(propertyName);
+ } else {
+ // property not found
+ return null;
+ }
+ }
+
int getPropertyIndex(String propertyName) {
- if (!sPropertyMap.containsKey(propertyName)) {
+ if (!sPropertyIndexMap.containsKey(propertyName)) {
return -1;
} else {
- return sPropertyMap.get(propertyName);
+ return sPropertyIndexMap.get(propertyName);
}
}
@@ -1789,6 +2085,7 @@
abstract boolean onStateChange(int[] state);
abstract boolean isStateful();
abstract int getNativeSize();
+ abstract Property getProperty(String propertyName);
}
private static native long nCreateTree(long rootGroupPtr);
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index 8c20ddc..f36c00c 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -228,7 +228,7 @@
if (exportResult.resultCode != KeyStore.NO_ERROR) {
throw (UnrecoverableKeyException)
new UnrecoverableKeyException("Failed to obtain X.509 form of public key")
- .initCause(KeyStore.getKeyStoreException(errorCode));
+ .initCause(KeyStore.getKeyStoreException(exportResult.resultCode));
}
final byte[] x509EncodedPublicKey = exportResult.exportData;
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index c9d4af6..366ef71 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -113,6 +113,10 @@
-DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\" \
-Wall -Wno-unused-parameter -Wunreachable-code -Werror
+ifeq ($(TARGET_USES_HWC2),true)
+ hwui_cflags += -DUSE_HWC2
+endif
+
# GCC false-positives on this warning, and since we -Werror that's
# a problem
hwui_cflags += -Wno-free-nonheap-object
diff --git a/libs/hwui/AnimationContext.h b/libs/hwui/AnimationContext.h
index 909ed36..11d305c 100644
--- a/libs/hwui/AnimationContext.h
+++ b/libs/hwui/AnimationContext.h
@@ -100,6 +100,8 @@
ANDROID_API virtual void destroy();
+ ANDROID_API virtual void pauseAnimators() {}
+
private:
friend class AnimationHandle;
void addAnimationHandle(AnimationHandle* handle);
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 4d65782..74aa303 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -123,22 +123,27 @@
mPlayTime = (mPlayState == PlayState::Running || mPlayState == PlayState::Reversing) ?
mPlayTime : 0;
mPlayState = PlayState::Running;
+ mPendingActionUponFinish = Action::None;
break;
case Request::Reverse:
mPlayTime = (mPlayState == PlayState::Running || mPlayState == PlayState::Reversing) ?
mPlayTime : mDuration;
mPlayState = PlayState::Reversing;
+ mPendingActionUponFinish = Action::None;
break;
case Request::Reset:
mPlayTime = 0;
mPlayState = PlayState::Finished;
+ mPendingActionUponFinish = Action::Reset;
break;
case Request::Cancel:
mPlayState = PlayState::Finished;
+ mPendingActionUponFinish = Action::None;
break;
case Request::End:
mPlayTime = mPlayState == PlayState::Reversing ? 0 : mDuration;
mPlayState = PlayState::Finished;
+ mPendingActionUponFinish = Action::End;
break;
default:
LOG_ALWAYS_FATAL("Invalid staging request: %d", static_cast<int>(request));
@@ -176,8 +181,6 @@
mStagingRequests.clear();
if (mStagingPlayState == PlayState::Finished) {
- // Set the staging play time and end the animation
- updatePlayTime(mPlayTime);
callOnFinishedListener(context);
} else if (mStagingPlayState == PlayState::Running
|| mStagingPlayState == PlayState::Reversing) {
@@ -236,6 +239,15 @@
return false;
}
if (mPlayState == PlayState::Finished) {
+ if (mPendingActionUponFinish == Action::Reset) {
+ // Skip to start.
+ updatePlayTime(0);
+ } else if (mPendingActionUponFinish == Action::End) {
+ // Skip to end.
+ updatePlayTime(mDuration);
+ }
+ // Reset pending action.
+ mPendingActionUponFinish = Action ::None;
return true;
}
@@ -274,6 +286,10 @@
return playTime >= mDuration;
}
+nsecs_t BaseRenderNodeAnimator::getRemainingPlayTime() {
+ return mPlayState == PlayState::Reversing ? mPlayTime : mDuration - mPlayTime;
+}
+
void BaseRenderNodeAnimator::forceEndNow(AnimationContext& context) {
if (mPlayState < PlayState::Finished) {
mPlayState = PlayState::Finished;
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index fdae0f3..72bac6c 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -44,6 +44,12 @@
ANDROID_API virtual ~AnimationListener() {}
};
+enum class RepeatMode {
+ // These are the same values as the RESTART and REVERSE in ValueAnimator.java.
+ Restart = 1,
+ Reverse = 2
+};
+
class BaseRenderNodeAnimator : public VirtualLightRefBase {
PREVENT_COPY_AND_ASSIGN(BaseRenderNodeAnimator);
public:
@@ -62,19 +68,23 @@
}
bool mayRunAsync() { return mMayRunAsync; }
ANDROID_API void start();
- ANDROID_API void reset();
+ ANDROID_API virtual void reset();
ANDROID_API void reverse();
// Terminates the animation at its current progress.
ANDROID_API void cancel();
// Terminates the animation and skip to the end of the animation.
- ANDROID_API void end();
+ ANDROID_API virtual void end();
void attach(RenderNode* target);
virtual void onAttached() {}
void detach() { mTarget = nullptr; }
- void pushStaging(AnimationContext& context);
- bool animate(AnimationContext& context);
+ ANDROID_API void pushStaging(AnimationContext& context);
+ ANDROID_API bool animate(AnimationContext& context);
+
+ // Returns the remaining time in ms for the animation. Note this should only be called during
+ // an animation on RenderThread.
+ ANDROID_API nsecs_t getRemainingPlayTime();
bool isRunning() { return mPlayState == PlayState::Running
|| mPlayState == PlayState::Reversing; }
@@ -155,6 +165,17 @@
Cancel,
End
};
+
+ // Defines different actions upon finish.
+ enum class Action {
+ // For animations that got canceled or finished normally. no more action needs to be done.
+ None,
+ // For animations that get reset, the reset will happen in the next animation pulse.
+ Reset,
+ // For animations being ended, in the next animation pulse the animation will skip to end.
+ End
+ };
+
inline void checkMutable();
virtual void transitionToRunning(AnimationContext& context);
void doSetStartValue(float value);
@@ -162,7 +183,7 @@
void resolveStagingRequest(Request request);
std::vector<Request> mStagingRequests;
-
+ Action mPendingActionUponFinish = Action::None;
};
class RenderPropertyAnimator : public BaseRenderNodeAnimator {
diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp
index fe68239..84451ba 100644
--- a/libs/hwui/ClipArea.cpp
+++ b/libs/hwui/ClipArea.cpp
@@ -464,10 +464,7 @@
}
case ClipMode::Region:
other = getRegion(recordedClip);
-
- // TODO: handle non-translate transforms properly!
- other.translate(recordedClipTransform.getTranslateX(),
- recordedClipTransform.getTranslateY());
+ applyTransformToRegion(recordedClipTransform, &other);
}
ClipRegion* regionClip = allocator.create<ClipRegion>();
@@ -527,11 +524,29 @@
}
} else {
SkRegion region(getRegion(clip));
- // TODO: handle non-translate transforms properly!
- region.translate(transform.getTranslateX(), transform.getTranslateY());
+ applyTransformToRegion(transform, ®ion);
clipRegion(region, SkRegion::kIntersect_Op);
}
}
+void ClipArea::applyTransformToRegion(const Matrix4& transform, SkRegion* region) {
+ if (transform.rectToRect() && !transform.isPureTranslate()) {
+ // handle matrices with scale manually by mapping each rect
+ SkRegion other;
+ SkRegion::Iterator it(*region);
+ while (!it.done()) {
+ Rect rect(it.rect());
+ transform.mapRect(rect);
+ rect.snapGeometryToPixelBoundaries(true);
+ other.op(rect.left, rect.top, rect.right, rect.bottom, SkRegion::kUnion_Op);
+ it.next();
+ }
+ region->swap(other);
+ } else {
+ // TODO: handle non-translate transforms properly!
+ region->translate(transform.getTranslateX(), transform.getTranslateY());
+ }
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h
index 6eb2eef..53d9d03 100644
--- a/libs/hwui/ClipArea.h
+++ b/libs/hwui/ClipArea.h
@@ -179,6 +179,8 @@
const ClipBase* recordedClip, const Matrix4& recordedClipTransform);
void applyClip(const ClipBase* recordedClip, const Matrix4& recordedClipTransform);
+ static void applyTransformToRegion(const Matrix4& transform, SkRegion* region);
+
private:
void enterRectangleMode();
void rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index b572bda..28be05c 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -45,7 +45,7 @@
, regions(stdAllocator)
, referenceHolders(stdAllocator)
, functors(stdAllocator)
- , pushStagingFunctors(stdAllocator)
+ , vectorDrawables(stdAllocator)
, hasDrawOps(false) {
}
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 5b3227b..ccf71c6 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -69,6 +69,11 @@
typedef DrawRenderNodeOp NodeOpType;
#endif
+namespace VectorDrawable {
+class Tree;
+};
+typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
+
/**
* Holds data used in the playback a tree of DisplayLists.
*/
@@ -110,16 +115,6 @@
LinearAllocator mReplayAllocator;
};
-/**
- * Functor that can be used for objects with data in both UI thread and RT to keep the data
- * in sync. This functor, when added to DisplayList, will be call during DisplayList sync.
- */
-struct PushStagingFunctor {
- PushStagingFunctor() {}
- virtual ~PushStagingFunctor() {}
- virtual void operator ()() {}
-};
-
struct FunctorContainer {
Functor* functor;
GlFunctorLifecycleListener* listener;
@@ -161,7 +156,7 @@
const LsaVector<const SkBitmap*>& getBitmapResources() const { return bitmapResources; }
const LsaVector<FunctorContainer>& getFunctors() const { return functors; }
- const LsaVector<PushStagingFunctor*>& getPushStagingFunctors() { return pushStagingFunctors; }
+ const LsaVector<VectorDrawableRoot*>& getVectorDrawables() { return vectorDrawables; }
size_t addChild(NodeOpType* childOp);
@@ -203,10 +198,10 @@
// List of functors
LsaVector<FunctorContainer> functors;
- // List of functors that need to be notified of pushStaging. Note that this list gets nothing
+ // List of VectorDrawables that need to be notified of pushStaging. Note that this list gets nothing
// but a callback during sync DisplayList, unlike the list of functors defined above, which
// gets special treatment exclusive for webview.
- LsaVector<PushStagingFunctor*> pushStagingFunctors;
+ LsaVector<VectorDrawableRoot*> vectorDrawables;
bool hasDrawOps; // only used if !HWUI_NEW_OPS
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index ca968ce..bec66295 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -417,7 +417,7 @@
void DisplayListCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
mDisplayList->ref(tree);
- mDisplayList->pushStagingFunctors.push_back(tree->getFunctor());
+ mDisplayList->vectorDrawables.push_back(tree);
addDrawOp(new (alloc()) DrawVectorDrawableOp(tree, tree->stagingProperties()->getBounds()));
}
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
index 41e2233..826f0bb 100644
--- a/libs/hwui/FrameInfo.cpp
+++ b/libs/hwui/FrameInfo.cpp
@@ -35,8 +35,17 @@
"IssueDrawCommandsStart",
"SwapBuffers",
"FrameCompleted",
+ "DequeueBufferDuration",
+ "QueueBufferDuration",
};
+static_assert((sizeof(FrameInfoNames)/sizeof(FrameInfoNames[0]))
+ == static_cast<int>(FrameInfoIndex::NumIndexes),
+ "size mismatch: FrameInfoNames doesn't match the enum!");
+
+static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 16,
+ "Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)");
+
void FrameInfo::importUiThreadInfo(int64_t* info) {
memcpy(mFrameInfo, info, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
}
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index 0baca39..45b57de 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -48,7 +48,11 @@
SwapBuffers,
FrameCompleted,
+ DequeueBufferDuration,
+ QueueBufferDuration,
+
// Must be the last value!
+ // Also must be kept in sync with FrameMetrics.java#FRAME_STATS_COUNT
NumIndexes
};
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index ebe9c42..ed6b211 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -16,6 +16,7 @@
#include "JankTracker.h"
#include "Properties.h"
+#include "utils/TimeUtils.h"
#include <algorithm>
#include <cutils/ashmem.h>
@@ -119,11 +120,27 @@
return index;
}
-JankTracker::JankTracker(nsecs_t frameIntervalNanos) {
+JankTracker::JankTracker(const DisplayInfo& displayInfo) {
// By default this will use malloc memory. It may be moved later to ashmem
// if there is shared space for it and a request comes in to do that.
mData = new ProfileData;
reset();
+ nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1_s / displayInfo.fps);
+#if USE_HWC2
+ nsecs_t sfOffset = frameIntervalNanos - (displayInfo.presentationDeadline - 1_ms);
+ nsecs_t offsetDelta = sfOffset - displayInfo.appVsyncOffset;
+ // There are two different offset cases. If the offsetDelta is positive
+ // and small, then the intention is to give apps extra time by leveraging
+ // pipelining between the UI & RT threads. If the offsetDelta is large or
+ // negative, the intention is to subtract time from the total duration
+ // in which case we can't afford to wait for dequeueBuffer blockage.
+ if (offsetDelta <= 4_ms && offsetDelta >= 0) {
+ // SF will begin composition at VSYNC-app + offsetDelta. If we are triple
+ // buffered, this is the expected time at which dequeueBuffer will
+ // return due to the staggering of VSYNC-app & VSYNC-sf.
+ mDequeueTimeForgiveness = offsetDelta + 4_ms;
+ }
+#endif
setFrameInterval(frameIntervalNanos);
}
@@ -213,6 +230,19 @@
mData->totalFrameCount++;
// Fast-path for jank-free frames
int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::FrameCompleted);
+ if (mDequeueTimeForgiveness
+ && frame[FrameInfoIndex::DequeueBufferDuration] > 500_us) {
+ nsecs_t expectedDequeueDuration =
+ mDequeueTimeForgiveness + frame[FrameInfoIndex::Vsync]
+ - frame[FrameInfoIndex::IssueDrawCommandsStart];
+ if (expectedDequeueDuration > 0) {
+ // Forgive only up to the expected amount, but not more than
+ // the actual time spent blocked.
+ nsecs_t forgiveAmount = std::min(expectedDequeueDuration,
+ frame[FrameInfoIndex::DequeueBufferDuration]);
+ totalDuration -= forgiveAmount;
+ }
+ }
uint32_t framebucket = frameCountIndexForFrameTime(totalDuration);
// Keep the fast path as fast as possible.
if (CC_LIKELY(totalDuration < mFrameInterval)) {
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index 84b8c3f..126025c 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -21,6 +21,7 @@
#include "utils/RingBuffer.h"
#include <cutils/compiler.h>
+#include <ui/DisplayInfo.h>
#include <array>
#include <memory>
@@ -56,7 +57,7 @@
// TODO: Replace DrawProfiler with this
class JankTracker {
public:
- JankTracker(nsecs_t frameIntervalNanos);
+ JankTracker(const DisplayInfo& displayInfo);
~JankTracker();
void addFrame(const FrameInfo& frame);
@@ -79,6 +80,14 @@
std::array<int64_t, NUM_BUCKETS> mThresholds;
int64_t mFrameInterval;
+ // The amount of time we will erase from the total duration to account
+ // for SF vsync offsets with HWC2 blocking dequeueBuffers.
+ // (Vsync + mDequeueBlockTolerance) is the point at which we expect
+ // SF to have released the buffer normally, so we will forgive up to that
+ // point in time by comparing to (IssueDrawCommandsStart + DequeueDuration)
+ // This is only used if we are in pipelined mode and are using HWC2,
+ // otherwise it's 0.
+ nsecs_t mDequeueTimeForgiveness = 0;
ProfileData* mData;
bool mIsMapped = false;
};
diff --git a/libs/hwui/PropertyValuesAnimatorSet.cpp b/libs/hwui/PropertyValuesAnimatorSet.cpp
index b29f91f..38fb70a 100644
--- a/libs/hwui/PropertyValuesAnimatorSet.cpp
+++ b/libs/hwui/PropertyValuesAnimatorSet.cpp
@@ -23,13 +23,17 @@
namespace uirenderer {
void PropertyValuesAnimatorSet::addPropertyAnimator(PropertyValuesHolder* propertyValuesHolder,
- Interpolator* interpolator, nsecs_t startDelay,
- nsecs_t duration, int repeatCount) {
+ Interpolator* interpolator, nsecs_t startDelay, nsecs_t duration, int repeatCount,
+ RepeatMode repeatMode) {
PropertyAnimator* animator = new PropertyAnimator(propertyValuesHolder,
- interpolator, startDelay, duration, repeatCount);
+ interpolator, startDelay, duration, repeatCount, repeatMode);
mAnimators.emplace_back(animator);
- setListener(new PropertyAnimatorSetListener(this));
+
+ // Check whether any child animator is infinite after adding it them to the set.
+ if (repeatCount == -1) {
+ mIsInfinite = true;
+ }
}
PropertyValuesAnimatorSet::PropertyValuesAnimatorSet()
@@ -37,6 +41,7 @@
setStartValue(0);
mLastFraction = 0.0f;
setInterpolator(new LinearInterpolator());
+ setListener(new PropertyAnimatorSetListener(this));
}
void PropertyValuesAnimatorSet::onFinished(BaseRenderNodeAnimator* animator) {
@@ -61,14 +66,9 @@
// Note that this set may containing animators modifying the same property, so when we
// reset the animators, we need to make sure the animators that end the first will
// have the final say on what the property value should be.
- (*it)->setFraction(0);
+ (*it)->setFraction(0, 0);
}
- } else if (playTime >= mDuration) {
- // Skip all the animators to end
- for (auto& anim : mAnimators) {
- anim->setFraction(1);
- }
- } else {
+ } else {
for (auto& anim : mAnimators) {
anim->setCurrentPlayTime(playTime);
}
@@ -78,15 +78,27 @@
void PropertyValuesAnimatorSet::start(AnimationListener* listener) {
init();
mOneShotListener = listener;
+ mRequestId++;
BaseRenderNodeAnimator::start();
}
void PropertyValuesAnimatorSet::reverse(AnimationListener* listener) {
init();
mOneShotListener = listener;
+ mRequestId++;
BaseRenderNodeAnimator::reverse();
}
+void PropertyValuesAnimatorSet::reset() {
+ mRequestId++;
+ BaseRenderNodeAnimator::reset();
+}
+
+void PropertyValuesAnimatorSet::end() {
+ mRequestId++;
+ BaseRenderNodeAnimator::end();
+}
+
void PropertyValuesAnimatorSet::init() {
if (mInitialized) {
return;
@@ -98,7 +110,7 @@
std::sort(mAnimators.begin(), mAnimators.end(), [](auto& a, auto&b) {
return a->getTotalDuration() < b->getTotalDuration();
});
- mDuration = mAnimators[mAnimators.size() - 1]->getTotalDuration();
+ mDuration = mAnimators.empty() ? 0 : mAnimators[mAnimators.size() - 1]->getTotalDuration();
mInitialized = true;
}
@@ -107,7 +119,8 @@
}
PropertyAnimator::PropertyAnimator(PropertyValuesHolder* holder, Interpolator* interpolator,
- nsecs_t startDelay, nsecs_t duration, int repeatCount)
+ nsecs_t startDelay, nsecs_t duration, int repeatCount,
+ RepeatMode repeatMode)
: mPropertyValuesHolder(holder), mInterpolator(interpolator), mStartDelay(startDelay),
mDuration(duration) {
if (repeatCount < 0) {
@@ -115,24 +128,44 @@
} else {
mRepeatCount = repeatCount;
}
+ mRepeatMode = repeatMode;
mTotalDuration = ((nsecs_t) mRepeatCount + 1) * mDuration + mStartDelay;
}
void PropertyAnimator::setCurrentPlayTime(nsecs_t playTime) {
- if (playTime >= mStartDelay && playTime < mTotalDuration) {
- nsecs_t currentIterationPlayTime = (playTime - mStartDelay) % mDuration;
- float fraction = currentIterationPlayTime / (float) mDuration;
- setFraction(fraction);
- } else if (mLatestFraction < 1.0f && playTime >= mTotalDuration) {
- // This makes sure we only set the fraction = 1 once. It is needed because there might
- // be another animator modifying the same property after this animator finishes, we need
- // to make sure we don't set conflicting values on the same property within one frame.
- setFraction(1.0f);
+ if (playTime < mStartDelay) {
+ return;
}
+
+ float currentIterationFraction;
+ long iteration;
+ if (playTime >= mTotalDuration) {
+ // Reached the end of the animation.
+ iteration = mRepeatCount;
+ currentIterationFraction = 1.0f;
+ } else {
+ // play time here is in range [mStartDelay, mTotalDuration)
+ iteration = (playTime - mStartDelay) / mDuration;
+ currentIterationFraction = ((playTime - mStartDelay) % mDuration) / (float) mDuration;
+ }
+ setFraction(currentIterationFraction, iteration);
}
-void PropertyAnimator::setFraction(float fraction) {
- mLatestFraction = fraction;
+void PropertyAnimator::setFraction(float fraction, long iteration) {
+ double totalFraction = fraction + iteration;
+ // This makes sure we only set the fraction = repeatCount + 1 once. It is needed because there
+ // might be another animator modifying the same property after this animator finishes, we need
+ // to make sure we don't set conflicting values on the same property within one frame.
+ if ((mLatestFraction == mRepeatCount + 1.0) && (totalFraction >= mRepeatCount + 1.0)) {
+ return;
+ }
+
+ mLatestFraction = totalFraction;
+ // Check the play direction (i.e. reverse or restart) every other iteration, and calculate the
+ // fraction based on the play direction.
+ if (iteration % 2 && mRepeatMode == RepeatMode::Reverse) {
+ fraction = 1.0f - fraction;
+ }
float interpolatedFraction = mInterpolator->interpolate(fraction);
mPropertyValuesHolder->setFraction(interpolatedFraction);
}
diff --git a/libs/hwui/PropertyValuesAnimatorSet.h b/libs/hwui/PropertyValuesAnimatorSet.h
index c7ae7c0..e208b08 100644
--- a/libs/hwui/PropertyValuesAnimatorSet.h
+++ b/libs/hwui/PropertyValuesAnimatorSet.h
@@ -26,12 +26,13 @@
class PropertyAnimator {
public:
PropertyAnimator(PropertyValuesHolder* holder, Interpolator* interpolator, nsecs_t startDelay,
- nsecs_t duration, int repeatCount);
+ nsecs_t duration, int repeatCount, RepeatMode repeatMode);
void setCurrentPlayTime(nsecs_t playTime);
nsecs_t getTotalDuration() {
return mTotalDuration;
}
- void setFraction(float fraction);
+ // fraction range: [0, 1], iteration range [0, repeatCount]
+ void setFraction(float fraction, long iteration);
private:
std::unique_ptr<PropertyValuesHolder> mPropertyValuesHolder;
@@ -40,9 +41,11 @@
nsecs_t mDuration;
uint32_t mRepeatCount;
nsecs_t mTotalDuration;
- float mLatestFraction = 0.0f;
+ RepeatMode mRepeatMode;
+ double mLatestFraction = 0;
};
+// TODO: This class should really be named VectorDrawableAnimator
class ANDROID_API PropertyValuesAnimatorSet : public BaseRenderNodeAnimator {
public:
friend class PropertyAnimatorSetListener;
@@ -50,11 +53,19 @@
void start(AnimationListener* listener);
void reverse(AnimationListener* listener);
+ virtual void reset() override;
+ virtual void end() override;
void addPropertyAnimator(PropertyValuesHolder* propertyValuesHolder,
Interpolator* interpolators, int64_t startDelays,
- nsecs_t durations, int repeatCount);
+ nsecs_t durations, int repeatCount, RepeatMode repeatMode);
virtual uint32_t dirtyMask();
+ bool isInfinite() { return mIsInfinite; }
+ void setVectorDrawable(VectorDrawableRoot* vd) { mVectorDrawable = vd; }
+ VectorDrawableRoot* getVectorDrawable() const { return mVectorDrawable.get(); }
+ AnimationListener* getOneShotListener() { return mOneShotListener.get(); }
+ void clearOneShotListener() { mOneShotListener = nullptr; }
+ uint32_t getRequestId() const { return mRequestId; }
protected:
virtual float getValue(RenderNode* target) const override;
@@ -69,6 +80,11 @@
std::vector< std::unique_ptr<PropertyAnimator> > mAnimators;
float mLastFraction = 0.0f;
bool mInitialized = false;
+ sp<VectorDrawableRoot> mVectorDrawable;
+ bool mIsInfinite = false;
+ // This request id gets incremented (on UI thread only) when a new request to modfiy the
+ // lifecycle of an animation happens, namely when start/end/reset/reverse is called.
+ uint32_t mRequestId = 0;
};
class PropertyAnimatorSetListener : public AnimationListener {
diff --git a/libs/hwui/PropertyValuesHolder.cpp b/libs/hwui/PropertyValuesHolder.cpp
index 0932d65..6ba0ab5 100644
--- a/libs/hwui/PropertyValuesHolder.cpp
+++ b/libs/hwui/PropertyValuesHolder.cpp
@@ -25,7 +25,27 @@
using namespace VectorDrawable;
-float PropertyValuesHolder::getValueFromData(float fraction) {
+inline U8CPU lerp(U8CPU fromValue, U8CPU toValue, float fraction) {
+ return (U8CPU) (fromValue * (1 - fraction) + toValue * fraction);
+}
+
+// TODO: Add a test for this
+void ColorEvaluator::evaluate(SkColor* outColor,
+ const SkColor& fromColor, const SkColor& toColor, float fraction) const {
+ U8CPU alpha = lerp(SkColorGetA(fromColor), SkColorGetA(toColor), fraction);
+ U8CPU red = lerp(SkColorGetR(fromColor), SkColorGetR(toColor), fraction);
+ U8CPU green = lerp(SkColorGetG(fromColor), SkColorGetG(toColor), fraction);
+ U8CPU blue = lerp(SkColorGetB(fromColor), SkColorGetB(toColor), fraction);
+ *outColor = SkColorSetARGB(alpha, red, green, blue);
+}
+
+void PathEvaluator::evaluate(PathData* out,
+ const PathData& from, const PathData& to, float fraction) const {
+ VectorDrawableUtils::interpolatePaths(out, from, to, fraction);
+}
+
+template<typename T>
+const T PropertyValuesHolderImpl<T>::getValueFromData(float fraction) const {
if (mDataSource.size() == 0) {
LOG_ALWAYS_FATAL("No data source is defined");
return 0;
@@ -41,57 +61,44 @@
int lowIndex = floor(fraction);
fraction -= lowIndex;
- float value = mDataSource[lowIndex] * (1.0f - fraction)
- + mDataSource[lowIndex + 1] * fraction;
+ T value;
+ mEvaluator->evaluate(&value, mDataSource[lowIndex], mDataSource[lowIndex + 1], fraction);
return value;
}
-void GroupPropertyValuesHolder::setFraction(float fraction) {
- float animatedValue;
+template<typename T>
+const T PropertyValuesHolderImpl<T>::calculateAnimatedValue(float fraction) const {
if (mDataSource.size() > 0) {
- animatedValue = getValueFromData(fraction);
+ return getValueFromData(fraction);
} else {
- animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction;
+ T value;
+ mEvaluator->evaluate(&value, mStartValue, mEndValue, fraction);
+ return value;
}
+}
+
+void GroupPropertyValuesHolder::setFraction(float fraction) {
+ float animatedValue = calculateAnimatedValue(fraction);
mGroup->mutateProperties()->setPropertyValue(mPropertyId, animatedValue);
}
-inline U8CPU lerp(U8CPU fromValue, U8CPU toValue, float fraction) {
- return (U8CPU) (fromValue * (1 - fraction) + toValue * fraction);
-}
-
-// TODO: Add a test for this
-SkColor FullPathColorPropertyValuesHolder::interpolateColors(SkColor fromColor, SkColor toColor,
- float fraction) {
- U8CPU alpha = lerp(SkColorGetA(fromColor), SkColorGetA(toColor), fraction);
- U8CPU red = lerp(SkColorGetR(fromColor), SkColorGetR(toColor), fraction);
- U8CPU green = lerp(SkColorGetG(fromColor), SkColorGetG(toColor), fraction);
- U8CPU blue = lerp(SkColorGetB(fromColor), SkColorGetB(toColor), fraction);
- return SkColorSetARGB(alpha, red, green, blue);
-}
-
void FullPathColorPropertyValuesHolder::setFraction(float fraction) {
- SkColor animatedValue = interpolateColors(mStartValue, mEndValue, fraction);
+ SkColor animatedValue = calculateAnimatedValue(fraction);
mFullPath->mutateProperties()->setColorPropertyValue(mPropertyId, animatedValue);
}
void FullPathPropertyValuesHolder::setFraction(float fraction) {
- float animatedValue;
- if (mDataSource.size() > 0) {
- animatedValue = getValueFromData(fraction);
- } else {
- animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction;
- }
+ float animatedValue = calculateAnimatedValue(fraction);
mFullPath->mutateProperties()->setPropertyValue(mPropertyId, animatedValue);
}
void PathDataPropertyValuesHolder::setFraction(float fraction) {
- VectorDrawableUtils::interpolatePaths(&mPathData, mStartValue, mEndValue, fraction);
+ mEvaluator->evaluate(&mPathData, mStartValue, mEndValue, fraction);
mPath->mutateProperties()->setData(mPathData);
}
void RootAlphaPropertyValuesHolder::setFraction(float fraction) {
- float animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction;
+ float animatedValue = calculateAnimatedValue(fraction);
mTree->mutateProperties()->setRootAlpha(animatedValue);
}
diff --git a/libs/hwui/PropertyValuesHolder.h b/libs/hwui/PropertyValuesHolder.h
index b905fae..432f8ba 100644
--- a/libs/hwui/PropertyValuesHolder.h
+++ b/libs/hwui/PropertyValuesHolder.h
@@ -31,91 +31,130 @@
class ANDROID_API PropertyValuesHolder {
public:
virtual void setFraction(float fraction) = 0;
- void setPropertyDataSource(float* dataSource, int length) {
- mDataSource.insert(mDataSource.begin(), dataSource, dataSource + length);
- }
- float getValueFromData(float fraction);
- virtual ~PropertyValuesHolder() {}
-protected:
- std::vector<float> mDataSource;
+ virtual ~PropertyValuesHolder() {}
};
-class ANDROID_API GroupPropertyValuesHolder : public PropertyValuesHolder {
+template <typename T>
+class Evaluator {
+public:
+ virtual void evaluate(T* out, const T& from, const T& to, float fraction) const {};
+ virtual ~Evaluator() {}
+};
+
+class FloatEvaluator : public Evaluator<float> {
+public:
+ virtual void evaluate(float* out, const float& from, const float& to, float fraction)
+ const override {
+ *out = from * (1 - fraction) + to * fraction;
+ }
+};
+
+class ANDROID_API ColorEvaluator : public Evaluator<SkColor> {
+public:
+ virtual void evaluate(SkColor* outColor, const SkColor& from, const SkColor& to,
+ float fraction) const override;
+};
+
+class ANDROID_API PathEvaluator : public Evaluator<PathData> {
+ virtual void evaluate(PathData* out, const PathData& from, const PathData& to, float fraction)
+ const override;
+};
+
+template <typename T>
+class ANDROID_API PropertyValuesHolderImpl : public PropertyValuesHolder {
+public:
+ PropertyValuesHolderImpl(const T& startValue, const T& endValue)
+ : mStartValue(startValue)
+ , mEndValue(endValue) {}
+ void setPropertyDataSource(T* dataSource, int length) {
+ mDataSource.insert(mDataSource.begin(), dataSource, dataSource + length);
+ }
+ // Calculate the animated value from the data source.
+ const T getValueFromData(float fraction) const;
+ // Convenient method to favor getting animated value from data source. If no data source is set
+ // fall back to linear interpolation.
+ const T calculateAnimatedValue(float fraction) const;
+protected:
+ std::unique_ptr<Evaluator<T>> mEvaluator = nullptr;
+ // This contains uniformly sampled data throughout the animation duration. The first element
+ // should be the start value and the last should be the end value of the animation. When the
+ // data source is set, we'll favor data source over the linear interpolation of start/end value
+ // for calculation of animated value.
+ std::vector<T> mDataSource;
+ T mStartValue;
+ T mEndValue;
+};
+
+class ANDROID_API GroupPropertyValuesHolder : public PropertyValuesHolderImpl<float> {
public:
GroupPropertyValuesHolder(VectorDrawable::Group* ptr, int propertyId, float startValue,
float endValue)
- : mGroup(ptr)
- , mPropertyId(propertyId)
- , mStartValue(startValue)
- , mEndValue(endValue){
+ : PropertyValuesHolderImpl(startValue, endValue)
+ , mGroup(ptr)
+ , mPropertyId(propertyId) {
+ mEvaluator.reset(new FloatEvaluator());
}
void setFraction(float fraction) override;
private:
VectorDrawable::Group* mGroup;
int mPropertyId;
- float mStartValue;
- float mEndValue;
};
-class ANDROID_API FullPathColorPropertyValuesHolder : public PropertyValuesHolder {
+class ANDROID_API FullPathColorPropertyValuesHolder : public PropertyValuesHolderImpl<SkColor> {
public:
- FullPathColorPropertyValuesHolder(VectorDrawable::FullPath* ptr, int propertyId, int32_t startValue,
- int32_t endValue)
- : mFullPath(ptr)
- , mPropertyId(propertyId)
- , mStartValue(startValue)
- , mEndValue(endValue) {};
+ FullPathColorPropertyValuesHolder(VectorDrawable::FullPath* ptr, int propertyId,
+ SkColor startValue, SkColor endValue)
+ : PropertyValuesHolderImpl(startValue, endValue)
+ , mFullPath(ptr)
+ , mPropertyId(propertyId) {
+ mEvaluator.reset(new ColorEvaluator());
+ }
void setFraction(float fraction) override;
static SkColor interpolateColors(SkColor fromColor, SkColor toColor, float fraction);
private:
VectorDrawable::FullPath* mFullPath;
int mPropertyId;
- int32_t mStartValue;
- int32_t mEndValue;
};
-class ANDROID_API FullPathPropertyValuesHolder : public PropertyValuesHolder {
+class ANDROID_API FullPathPropertyValuesHolder : public PropertyValuesHolderImpl<float> {
public:
FullPathPropertyValuesHolder(VectorDrawable::FullPath* ptr, int propertyId, float startValue,
float endValue)
- : mFullPath(ptr)
- , mPropertyId(propertyId)
- , mStartValue(startValue)
- , mEndValue(endValue) {};
+ : PropertyValuesHolderImpl(startValue, endValue)
+ , mFullPath(ptr)
+ , mPropertyId(propertyId) {
+ mEvaluator.reset(new FloatEvaluator());
+ };
void setFraction(float fraction) override;
private:
VectorDrawable::FullPath* mFullPath;
int mPropertyId;
- float mStartValue;
- float mEndValue;
};
-class ANDROID_API PathDataPropertyValuesHolder : public PropertyValuesHolder {
+class ANDROID_API PathDataPropertyValuesHolder : public PropertyValuesHolderImpl<PathData> {
public:
PathDataPropertyValuesHolder(VectorDrawable::Path* ptr, PathData* startValue,
PathData* endValue)
- : mPath(ptr)
- , mStartValue(*startValue)
- , mEndValue(*endValue) {};
+ : PropertyValuesHolderImpl(*startValue, *endValue)
+ , mPath(ptr) {
+ mEvaluator.reset(new PathEvaluator());
+ };
void setFraction(float fraction) override;
private:
VectorDrawable::Path* mPath;
PathData mPathData;
- PathData mStartValue;
- PathData mEndValue;
};
-class ANDROID_API RootAlphaPropertyValuesHolder : public PropertyValuesHolder {
+class ANDROID_API RootAlphaPropertyValuesHolder : public PropertyValuesHolderImpl<float> {
public:
RootAlphaPropertyValuesHolder(VectorDrawable::Tree* tree, float startValue, float endValue)
- : mTree(tree)
- , mStartValue(startValue)
- , mEndValue(endValue) {}
+ : PropertyValuesHolderImpl(startValue, endValue)
+ , mTree(tree) {
+ mEvaluator.reset(new FloatEvaluator());
+ }
void setFraction(float fraction) override;
private:
VectorDrawable::Tree* mTree;
- float mStartValue;
- float mEndValue;
};
}
}
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index b49f9b5..0c552ba 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -127,7 +127,8 @@
// operations will be able to store and restore the current clip and transform info, and
// quick rejection will be correct (for display lists)
- const Rect unmappedBounds(left, top, right, bottom);
+ Rect unmappedBounds(left, top, right, bottom);
+ unmappedBounds.roundOut();
// determine clipped bounds relative to previous viewport.
Rect visibleBounds = unmappedBounds;
@@ -148,50 +149,53 @@
// Map visible bounds back to layer space, and intersect with parameter bounds
Rect layerBounds = visibleBounds;
- Matrix4 inverse;
- inverse.loadInverse(*previous.transform);
- inverse.mapRect(layerBounds);
- layerBounds.doIntersect(unmappedBounds);
+ if (CC_LIKELY(!layerBounds.isEmpty())) {
+ // if non-empty, can safely map by the inverse transform
+ Matrix4 inverse;
+ inverse.loadInverse(*previous.transform);
+ inverse.mapRect(layerBounds);
+ layerBounds.doIntersect(unmappedBounds);
+ }
int saveValue = mState.save((int) flags);
Snapshot& snapshot = *mState.writableSnapshot();
// layerBounds is in original bounds space, but clipped by current recording clip
- if (layerBounds.isEmpty() || unmappedBounds.isEmpty()) {
- // Don't bother recording layer, since it's been rejected
+ if (!layerBounds.isEmpty() && !unmappedBounds.isEmpty()) {
if (CC_LIKELY(clippedLayer)) {
- snapshot.resetClip(0, 0, 0, 0);
+ auto previousClip = getRecordedClip(); // capture before new snapshot clip has changed
+ if (addOp(alloc().create_trivial<BeginLayerOp>(
+ unmappedBounds,
+ *previous.transform, // transform to *draw* with
+ previousClip, // clip to *draw* with
+ refPaint(paint))) >= 0) {
+ snapshot.flags |= Snapshot::kFlagIsLayer | Snapshot::kFlagIsFboLayer;
+ snapshot.initializeViewport(unmappedBounds.getWidth(), unmappedBounds.getHeight());
+ snapshot.transform->loadTranslate(-unmappedBounds.left, -unmappedBounds.top, 0.0f);
+
+ Rect clip = layerBounds;
+ clip.translate(-unmappedBounds.left, -unmappedBounds.top);
+ snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom);
+ snapshot.roundRectClipState = nullptr;
+ return saveValue;
+ }
+ } else {
+ if (addOp(alloc().create_trivial<BeginUnclippedLayerOp>(
+ unmappedBounds,
+ *mState.currentSnapshot()->transform,
+ getRecordedClip(),
+ refPaint(paint))) >= 0) {
+ snapshot.flags |= Snapshot::kFlagIsLayer;
+ return saveValue;
+ }
}
- return saveValue;
}
+ // Layer not needed, so skip recording it...
if (CC_LIKELY(clippedLayer)) {
- auto previousClip = getRecordedClip(); // note: done before new snapshot's clip has changed
-
- snapshot.flags |= Snapshot::kFlagIsLayer | Snapshot::kFlagIsFboLayer;
- snapshot.initializeViewport(unmappedBounds.getWidth(), unmappedBounds.getHeight());
- snapshot.transform->loadTranslate(-unmappedBounds.left, -unmappedBounds.top, 0.0f);
-
- Rect clip = layerBounds;
- clip.translate(-unmappedBounds.left, -unmappedBounds.top);
- snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom);
- snapshot.roundRectClipState = nullptr;
-
- addOp(alloc().create_trivial<BeginLayerOp>(
- unmappedBounds,
- *previous.transform, // transform to *draw* with
- previousClip, // clip to *draw* with
- refPaint(paint)));
- } else {
- snapshot.flags |= Snapshot::kFlagIsLayer;
-
- addOp(alloc().create_trivial<BeginUnclippedLayerOp>(
- unmappedBounds,
- *mState.currentSnapshot()->transform,
- getRecordedClip(),
- refPaint(paint)));
+ // ... and set empty clip to reject inner content, if possible
+ snapshot.resetClip(0, 0, 0, 0);
}
-
return saveValue;
}
@@ -267,7 +271,7 @@
// Geometry
void RecordingCanvas::drawPoints(const float* points, int floatCount, const SkPaint& paint) {
- if (floatCount < 2) return;
+ if (CC_UNLIKELY(floatCount < 2 || PaintUtils::paintWillNotDraw(paint))) return;
floatCount &= ~0x1; // round down to nearest two
addOp(alloc().create_trivial<PointsOp>(
@@ -278,7 +282,7 @@
}
void RecordingCanvas::drawLines(const float* points, int floatCount, const SkPaint& paint) {
- if (floatCount < 4) return;
+ if (CC_UNLIKELY(floatCount < 4 || PaintUtils::paintWillNotDraw(paint))) return;
floatCount &= ~0x3; // round down to nearest four
addOp(alloc().create_trivial<LinesOp>(
@@ -289,6 +293,8 @@
}
void RecordingCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) {
+ if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+
addOp(alloc().create_trivial<RectOp>(
Rect(left, top, right, bottom),
*(mState.currentSnapshot()->transform),
@@ -330,6 +336,8 @@
}
void RecordingCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
+ if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+
if (paint.getStyle() == SkPaint::kFill_Style
&& (!paint.isAntiAlias() || mState.currentTransform()->isSimple())) {
int count = 0;
@@ -354,8 +362,11 @@
}
}
}
+
void RecordingCanvas::drawRoundRect(float left, float top, float right, float bottom,
float rx, float ry, const SkPaint& paint) {
+ if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+
if (CC_LIKELY(MathUtils::isPositive(rx) || MathUtils::isPositive(ry))) {
addOp(alloc().create_trivial<RoundRectOp>(
Rect(left, top, right, bottom),
@@ -390,7 +401,8 @@
void RecordingCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
// TODO: move to Canvas.h
- if (radius <= 0) return;
+ if (CC_UNLIKELY(radius <= 0 || PaintUtils::paintWillNotDraw(paint))) return;
+
drawOval(x - radius, y - radius, x + radius, y + radius, paint);
}
@@ -410,6 +422,8 @@
}
void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
+ if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+
addOp(alloc().create_trivial<OvalOp>(
Rect(left, top, right, bottom),
*(mState.currentSnapshot()->transform),
@@ -419,6 +433,8 @@
void RecordingCanvas::drawArc(float left, float top, float right, float bottom,
float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
+ if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+
if (fabs(sweepAngle) >= 360.0f) {
drawOval(left, top, right, bottom, paint);
} else {
@@ -432,6 +448,8 @@
}
void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+ if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+
addOp(alloc().create_trivial<PathOp>(
Rect(path.getBounds()),
*(mState.currentSnapshot()->transform),
@@ -440,8 +458,8 @@
}
void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
- mDisplayList->pushStagingFunctors.push_back(tree->getFunctor());
mDisplayList->ref(tree);
+ mDisplayList->vectorDrawables.push_back(tree);
addOp(alloc().create_trivial<VectorDrawableOp>(
tree,
Rect(tree->stagingProperties()->getBounds()),
@@ -604,7 +622,7 @@
functor));
}
-size_t RecordingCanvas::addOp(RecordedOp* op) {
+int RecordingCanvas::addOp(RecordedOp* op) {
// skip op with empty clip
if (op->localClip && op->localClip->rect.isEmpty()) {
// NOTE: this rejection happens after op construction/content ref-ing, so content ref'd
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 372be24..337e97b 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -208,7 +208,7 @@
void drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint);
- size_t addOp(RecordedOp* op);
+ int addOp(RecordedOp* op);
// ----------------------------------------------------------------------------
// lazy object copy
// ----------------------------------------------------------------------------
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index de4fa55..5786668 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -73,6 +73,13 @@
bottom(height) {
}
+ inline Rect(const SkIRect& rect):
+ left(rect.fLeft),
+ top(rect.fTop),
+ right(rect.fRight),
+ bottom(rect.fBottom) {
+ }
+
inline Rect(const SkRect& rect):
left(rect.fLeft),
top(rect.fTop),
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index a393625..bdcad79 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -422,6 +422,16 @@
pushStagingDisplayListChanges(info);
}
prepareSubTree(info, childFunctorsNeedLayer, mDisplayList);
+
+ if (mDisplayList) {
+ for (auto& vectorDrawable : mDisplayList->getVectorDrawables()) {
+ // If any vector drawable in the display list needs update, damage the node.
+ if (vectorDrawable->isDirty()) {
+ damageSelf(info);
+ }
+ vectorDrawable->setPropertyChangeWillBeConsumed(true);
+ }
+ }
pushLayerUpdate(info);
info.damageAccumulator->popTransform();
@@ -482,8 +492,8 @@
for (auto& iter : mDisplayList->getFunctors()) {
(*iter.functor)(DrawGlInfo::kModeSync, nullptr);
}
- for (size_t i = 0; i < mDisplayList->getPushStagingFunctors().size(); i++) {
- (*mDisplayList->getPushStagingFunctors()[i])();
+ for (auto& vectorDrawable : mDisplayList->getVectorDrawables()) {
+ vectorDrawable->syncProperties();
}
}
}
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index f80be5e..47fef6d 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -232,7 +232,7 @@
// the frameNumber to appropriately batch/synchronize these transactions.
// There is no other filtering/batching to ensure that only the "final"
// state called once per frame.
- class ANDROID_API PositionListener {
+ class ANDROID_API PositionListener : public VirtualLightRefBase {
public:
virtual ~PositionListener() {}
// Called when the RenderNode's position changes
@@ -247,7 +247,7 @@
// before the RenderNode is used for drawing.
// RenderNode takes ownership of the pointer
ANDROID_API void setPositionListener(PositionListener* listener) {
- mPositionListener.reset(listener);
+ mPositionListener = listener;
}
// This is only modified in MODE_FULL, so it can be safely accessed
@@ -366,7 +366,7 @@
// mDisplayList, not mStagingDisplayList.
uint32_t mParentCount;
- std::unique_ptr<PositionListener> mPositionListener;
+ sp<PositionListener> mPositionListener;
}; // class RenderNode
} /* namespace uirenderer */
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index ade8600a..523924a 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -167,6 +167,10 @@
return texture;
}
+bool TextureCache::prefetch(const SkBitmap* bitmap) {
+ return getCachedTexture(bitmap, AtlasUsageType::Use);
+}
+
Texture* TextureCache::get(const SkBitmap* bitmap, AtlasUsageType atlasUsageType) {
Texture* texture = getCachedTexture(bitmap, atlasUsageType);
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index a4317ce..0a61b6b 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -78,6 +78,13 @@
bool prefetchAndMarkInUse(void* ownerToken, const SkBitmap* bitmap);
/**
+ * Attempts to precache the SkBitmap. Returns true if a Texture was successfully
+ * acquired for the bitmap, false otherwise. Does not mark the Texture
+ * as in use and won't update currently in-use Textures.
+ */
+ bool prefetch(const SkBitmap* bitmap);
+
+ /**
* Returns the texture associated with the specified bitmap from either within the cache, or
* the AssetAtlas. If the texture cannot be found in the cache, a new texture is generated.
*/
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index a5d1d4b..a0c3d9d 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -673,21 +673,17 @@
void onPropertyChanged(TreeProperties* prop);
TreeProperties* mutateStagingProperties() { return &mStagingProperties; }
const TreeProperties* stagingProperties() const { return &mStagingProperties; }
- PushStagingFunctor* getFunctor() { return &mFunctor;}
// This should only be called from animations on RT
TreeProperties* mutateProperties() { return &mProperties; }
+ // This should always be called from RT.
+ void markDirty() { mCache.dirty = true; }
+ bool isDirty() const { return mCache.dirty; }
+ bool getPropertyChangeWillBeConsumed() const { return mWillBeConsumed; }
+ void setPropertyChangeWillBeConsumed(bool willBeConsumed) { mWillBeConsumed = willBeConsumed; }
+
private:
- class VectorDrawableFunctor : public PushStagingFunctor {
- public:
- VectorDrawableFunctor(Tree* tree) : mTree(tree) {}
- virtual void operator ()() {
- mTree->syncProperties();
- }
- private:
- Tree* mTree;
- };
SkPaint* updatePaint(SkPaint* outPaint, TreeProperties* prop);
bool allocateBitmapIfNeeded(SkBitmap* outCache, int width, int height);
@@ -704,8 +700,6 @@
TreeProperties mProperties = TreeProperties(this);
TreeProperties mStagingProperties = TreeProperties(this);
- VectorDrawableFunctor mFunctor = VectorDrawableFunctor(this);
-
SkPaint mPaint;
struct Cache {
SkBitmap bitmap;
@@ -717,6 +711,8 @@
PropertyChangedListener mPropertyChangedListener
= PropertyChangedListener(&mCache.dirty, &mStagingCache.dirty);
+
+ mutable bool mWillBeConsumed = false;
};
} // namespace VectorDrawable
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index c626c54..dcaec42 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -67,7 +67,7 @@
, mEglManager(thread.eglManager())
, mOpaque(!translucent)
, mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
- , mJankTracker(thread.timeLord().frameIntervalNanos())
+ , mJankTracker(thread.mainDisplayInfo())
, mProfiler(mFrames)
, mContentDrawBounds(0, 0, 0, 0) {
mRenderNodes.emplace_back(rootRenderNode);
@@ -198,6 +198,48 @@
return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame);
}
+bool CanvasContext::isSwapChainStuffed() {
+ static const auto SLOW_THRESHOLD = 6_ms;
+
+ if (mSwapHistory.size() != mSwapHistory.capacity()) {
+ // We want at least 3 frames of history before attempting to
+ // guess if the queue is stuffed
+ return false;
+ }
+ nsecs_t frameInterval = mRenderThread.timeLord().frameIntervalNanos();
+ auto& swapA = mSwapHistory[0];
+
+ // Was there a happy queue & dequeue time? If so, don't
+ // consider it stuffed
+ if (swapA.dequeueDuration < SLOW_THRESHOLD
+ && swapA.queueDuration < SLOW_THRESHOLD) {
+ return false;
+ }
+
+ for (size_t i = 1; i < mSwapHistory.size(); i++) {
+ auto& swapB = mSwapHistory[i];
+
+ // If there's a multi-frameInterval gap we effectively already dropped a frame,
+ // so consider the queue healthy.
+ if (swapA.swapCompletedTime - swapB.swapCompletedTime > frameInterval * 3) {
+ return false;
+ }
+
+ // Was there a happy queue & dequeue time? If so, don't
+ // consider it stuffed
+ if (swapB.dequeueDuration < SLOW_THRESHOLD
+ && swapB.queueDuration < SLOW_THRESHOLD) {
+ return false;
+ }
+
+ swapA = swapB;
+ }
+
+ // All signs point to a stuffed swap chain
+ ATRACE_NAME("swap chain stuffed");
+ return true;
+}
+
void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
int64_t syncQueued, RenderNode* target) {
mRenderThread.removeFrameCallback(this);
@@ -243,7 +285,7 @@
if (CC_LIKELY(mSwapHistory.size())) {
nsecs_t latestVsync = mRenderThread.timeLord().latestVsync();
- const SwapHistory& lastSwap = mSwapHistory.back();
+ SwapHistory& lastSwap = mSwapHistory.back();
nsecs_t vsyncDelta = std::abs(lastSwap.vsyncTime - latestVsync);
// The slight fudge-factor is to deal with cases where
// the vsync was estimated due to being slow handling the signal.
@@ -253,15 +295,17 @@
// Already drew for this vsync pulse, UI draw request missed
// the deadline for RT animations
info.out.canDrawThisFrame = false;
- } else if (lastSwap.swapTime < latestVsync) {
+ } else if (vsyncDelta >= mRenderThread.timeLord().frameIntervalNanos() * 3
+ || (latestVsync - mLastDropVsync) < 500_ms) {
+ // It's been several frame intervals, assume the buffer queue is fine
+ // or the last drop was too recent
info.out.canDrawThisFrame = true;
} else {
- // We're maybe behind? Find out for sure
- int runningBehind = 0;
- // TODO: Have this method be on Surface, too, not just ANativeWindow...
- ANativeWindow* window = mNativeSurface.get();
- window->query(window, NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
- info.out.canDrawThisFrame = !runningBehind;
+ info.out.canDrawThisFrame = !isSwapChainStuffed();
+ if (!info.out.canDrawThisFrame) {
+ // dropping frame
+ mLastDropVsync = mRenderThread.timeLord().latestVsync();
+ }
}
} else {
info.out.canDrawThisFrame = true;
@@ -282,6 +326,7 @@
void CanvasContext::stopDrawing() {
mRenderThread.removeFrameCallback(this);
+ mAnimationContext->pauseAnimators();
}
void CanvasContext::notifyFramePending() {
@@ -515,10 +560,27 @@
}
SwapHistory& swap = mSwapHistory.next();
swap.damage = screenDirty;
- swap.swapTime = systemTime(CLOCK_MONOTONIC);
+ swap.swapCompletedTime = systemTime(CLOCK_MONOTONIC);
swap.vsyncTime = mRenderThread.timeLord().latestVsync();
+ if (mNativeSurface.get()) {
+ int durationUs;
+ mNativeSurface->query(NATIVE_WINDOW_LAST_DEQUEUE_DURATION, &durationUs);
+ swap.dequeueDuration = us2ns(durationUs);
+ mNativeSurface->query(NATIVE_WINDOW_LAST_QUEUE_DURATION, &durationUs);
+ swap.queueDuration = us2ns(durationUs);
+ } else {
+ swap.dequeueDuration = 0;
+ swap.queueDuration = 0;
+ }
+ mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration)
+ = swap.dequeueDuration;
+ mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration)
+ = swap.queueDuration;
mHaveNewSurface = false;
mFrameNumber = -1;
+ } else {
+ mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = 0;
+ mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = 0;
}
// TODO: Use a fence for real completion?
@@ -783,6 +845,7 @@
}
sp<FuncTask> task(new FuncTask());
task->func = func;
+ mFrameFences.push_back(task);
mFrameWorkProcessor->add(task);
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index a6eb7ad..3eef29b 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -180,6 +180,8 @@
void waitOnFences();
+ bool isSwapChainStuffed();
+
EGLint mLastFrameWidth = 0;
EGLint mLastFrameHeight = 0;
@@ -198,12 +200,17 @@
struct SwapHistory {
SkRect damage;
nsecs_t vsyncTime;
- nsecs_t swapTime;
+ nsecs_t swapCompletedTime;
+ nsecs_t dequeueDuration;
+ nsecs_t queueDuration;
};
RingBuffer<SwapHistory, 3> mSwapHistory;
int64_t mFrameNumber = -1;
+ // last vsync for a dropped frame due to stuffed queue
+ nsecs_t mLastDropVsync = 0;
+
bool mOpaque;
#if HWUI_NEW_OPS
BakedOpRenderer::LightInfo mLightInfo;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 54af282..a734401 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -22,9 +22,11 @@
#include "Readback.h"
#include "Rect.h"
#include "renderthread/CanvasContext.h"
+#include "renderthread/EglManager.h"
#include "renderthread/RenderTask.h"
#include "renderthread/RenderThread.h"
#include "utils/Macros.h"
+#include "utils/TimeUtils.h"
namespace android {
namespace uirenderer {
@@ -44,6 +46,8 @@
typedef struct { \
a1; a2; a3; a4; a5; a6; a7; a8; \
} ARGS(name); \
+ static_assert(std::is_trivially_destructible<ARGS(name)>::value, \
+ "Error, ARGS must be trivially destructible!"); \
static void* Bridge_ ## name(ARGS(name)* args)
#define SETUP_TASK(method) \
@@ -154,7 +158,7 @@
SETUP_TASK(updateSurface);
args->context = mContext;
args->surface = surface.get();
- postAndWait(task);
+ post(task);
}
CREATE_BRIDGE2(pauseSurface, CanvasContext* context, Surface* surface) {
@@ -514,6 +518,10 @@
post(task);
}
+int RenderProxy::getRenderThreadTid() {
+ return mRenderThread.getTid();
+}
+
CREATE_BRIDGE3(addRenderNode, CanvasContext* context, RenderNode* node, bool placeFront) {
args->context->addRenderNode(args->node, args->placeFront);
return nullptr;
@@ -632,6 +640,41 @@
reinterpret_cast<intptr_t>( staticPostAndWait(task) ));
}
+CREATE_BRIDGE2(prepareToDraw, RenderThread* thread, SkBitmap* bitmap) {
+ if (Caches::hasInstance() && args->thread->eglManager().hasEglContext()) {
+ ATRACE_NAME("Bitmap#prepareToDraw task");
+ Caches::getInstance().textureCache.prefetch(args->bitmap);
+ }
+ delete args->bitmap;
+ args->bitmap = nullptr;
+ return nullptr;
+}
+
+void RenderProxy::prepareToDraw(const SkBitmap& bitmap) {
+ // If we haven't spun up a hardware accelerated window yet, there's no
+ // point in precaching these bitmaps as it can't impact jank.
+ // We also don't know if we even will spin up a hardware-accelerated
+ // window or not.
+ if (!RenderThread::hasInstance()) return;
+ RenderThread* renderThread = &RenderThread::getInstance();
+ SETUP_TASK(prepareToDraw);
+ args->thread = renderThread;
+ args->bitmap = new SkBitmap(bitmap);
+ nsecs_t lastVsync = renderThread->timeLord().latestVsync();
+ nsecs_t estimatedNextVsync = lastVsync + renderThread->timeLord().frameIntervalNanos();
+ nsecs_t timeToNextVsync = estimatedNextVsync - systemTime(CLOCK_MONOTONIC);
+ // We expect the UI thread to take 4ms and for RT to be active from VSYNC+4ms to
+ // VSYNC+12ms or so, so aim for the gap during which RT is expected to
+ // be idle
+ // TODO: Make this concept a first-class supported thing? RT could use
+ // knowledge of pending draws to better schedule this task
+ if (timeToNextVsync > -6_ms && timeToNextVsync < 1_ms) {
+ renderThread->queueAt(task, estimatedNextVsync + 8_ms);
+ } else {
+ renderThread->queue(task);
+ }
+}
+
void RenderProxy::post(RenderTask* task) {
mRenderThread.queue(task);
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 898b314..bb111bd 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -115,6 +115,7 @@
ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size);
ANDROID_API void setProcessStatsBuffer(int fd);
+ ANDROID_API int getRenderThreadTid();
ANDROID_API void serializeDisplayListTree();
@@ -128,6 +129,7 @@
ANDROID_API long getDroppedFrameReportCount();
ANDROID_API static int copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap);
+ ANDROID_API static void prepareToDraw(const SkBitmap& bitmap);
private:
RenderThread& mRenderThread;
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 9fb30c9..f4b4416 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -190,7 +190,7 @@
initializeDisplayEventReceiver();
mEglManager = new EglManager(*this);
mRenderState = new RenderState(*this);
- mJankTracker = new JankTracker(frameIntervalNanos);
+ mJankTracker = new JankTracker(mDisplayInfo);
}
int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp
index 146e735..9956975 100644
--- a/libs/hwui/tests/common/TestContext.cpp
+++ b/libs/hwui/tests/common/TestContext.cpp
@@ -33,7 +33,6 @@
false, // secure?
0, // appVsyncOffset
0, // presentationDeadline
- 0, // colorTransform
};
DisplayInfo getBuiltInDisplay() {
diff --git a/libs/hwui/tests/unit/ClipAreaTests.cpp b/libs/hwui/tests/unit/ClipAreaTests.cpp
index 54ca68d..d4d7919 100644
--- a/libs/hwui/tests/unit/ClipAreaTests.cpp
+++ b/libs/hwui/tests/unit/ClipAreaTests.cpp
@@ -275,5 +275,73 @@
}
}
+TEST(ClipArea, serializeIntersectedClip_scale) {
+ ClipArea area(createClipArea());
+ area.setClip(0, 0, 400, 400);
+ LinearAllocator allocator;
+
+ SkPath circlePath;
+ circlePath.addCircle(50, 50, 50);
+
+ ClipRegion recordedClip;
+ recordedClip.region.setPath(circlePath, SkRegion(SkIRect::MakeWH(100, 100)));
+ recordedClip.rect = Rect(100, 100);
+
+ Matrix4 translateScale;
+ translateScale.loadTranslate(100, 100, 0);
+ translateScale.scale(2, 2, 1);
+ auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale);
+
+ ASSERT_NE(nullptr, resolvedClip);
+ EXPECT_EQ(ClipMode::Region, resolvedClip->mode);
+ EXPECT_EQ(Rect(100, 100, 300, 300), resolvedClip->rect);
+ auto clipRegion = reinterpret_cast<const ClipRegion*>(resolvedClip);
+ EXPECT_EQ(SkIRect::MakeLTRB(100, 100, 300, 300), clipRegion->region.getBounds());
+}
+
+TEST(ClipArea, applyTransformToRegion_identity) {
+ SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
+ ClipArea::applyTransformToRegion(Matrix4::identity(), ®ion);
+ EXPECT_TRUE(region.isRect());
+ EXPECT_EQ(SkIRect::MakeLTRB(1, 2, 3, 4), region.getBounds());
+}
+
+TEST(ClipArea, applyTransformToRegion_translate) {
+ SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
+ Matrix4 transform;
+ transform.loadTranslate(10, 20, 0);
+ ClipArea::applyTransformToRegion(transform, ®ion);
+ EXPECT_TRUE(region.isRect());
+ EXPECT_EQ(SkIRect::MakeLTRB(11, 22, 13, 24), region.getBounds());
+}
+
+TEST(ClipArea, applyTransformToRegion_scale) {
+ SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
+ Matrix4 transform;
+ transform.loadScale(2, 3, 1);
+ ClipArea::applyTransformToRegion(transform, ®ion);
+ EXPECT_TRUE(region.isRect());
+ EXPECT_EQ(SkIRect::MakeLTRB(2, 6, 6, 12), region.getBounds());
+}
+
+TEST(ClipArea, applyTransformToRegion_translateScale) {
+ SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
+ Matrix4 transform;
+ transform.translate(10, 20);
+ transform.scale(2, 3, 1);
+ ClipArea::applyTransformToRegion(transform, ®ion);
+ EXPECT_TRUE(region.isRect());
+ EXPECT_EQ(SkIRect::MakeLTRB(12, 26, 16, 32), region.getBounds());
+}
+
+TEST(ClipArea, applyTransformToRegion_rotate90) {
+ SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
+ Matrix4 transform;
+ transform.loadRotate(90);
+ ClipArea::applyTransformToRegion(transform, ®ion);
+ EXPECT_TRUE(region.isRect());
+ EXPECT_EQ(SkIRect::MakeLTRB(-4, 1, -2, 3), region.getBounds());
+}
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index af54e07..53dbede 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -1459,7 +1459,8 @@
static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) {
SkPaint paint;
- paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel
+ // order put in blue channel, transparent so overlapped content doesn't get rejected
+ paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder));
canvas->drawRect(0, 0, 100, 100, paint);
}
static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) {
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index 18171de..c072d0b 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -81,6 +81,27 @@
ASSERT_EQ(0u, dl->getOps().size()) << "Must be zero ops. Rect should be rejected.";
}
+TEST(RecordingCanvas, emptyPaintRejection) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ SkPaint emptyPaint;
+ emptyPaint.setColor(Color::Transparent);
+
+ float points[] = {0, 0, 200, 200};
+ canvas.drawPoints(points, 4, emptyPaint);
+ canvas.drawLines(points, 4, emptyPaint);
+ canvas.drawRect(0, 0, 200, 200, emptyPaint);
+ canvas.drawRegion(SkRegion(SkIRect::MakeWH(200, 200)), emptyPaint);
+ canvas.drawRoundRect(0, 0, 200, 200, 10, 10, emptyPaint);
+ canvas.drawCircle(100, 100, 100, emptyPaint);
+ canvas.drawOval(0, 0, 200, 200, emptyPaint);
+ canvas.drawArc(0, 0, 200, 200, 0, 360, true, emptyPaint);
+ SkPath path;
+ path.addRect(0, 0, 200, 200);
+ canvas.drawPath(path, emptyPaint);
+ });
+ EXPECT_EQ(0u, dl->getOps().size()) << "Op should be rejected";
+}
+
TEST(RecordingCanvas, drawArc) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.drawArc(0, 0, 200, 200, 0, 180, true, SkPaint());
@@ -340,6 +361,36 @@
EXPECT_EQ(3, count);
}
+TEST(RecordingCanvas, saveLayer_rounding) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
+ canvas.saveLayerAlpha(10.25f, 10.75f, 89.25f, 89.75f, 128, SaveFlags::ClipToLayer);
+ canvas.drawRect(20, 20, 80, 80, SkPaint());
+ canvas.restore();
+ });
+ int count = 0;
+ playbackOps(*dl, [&count](const RecordedOp& op) {
+ Matrix4 expectedMatrix;
+ switch(count++) {
+ case 0:
+ EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId);
+ EXPECT_EQ(Rect(10, 10, 90, 90), op.unmappedBounds) << "Expect bounds rounded out";
+ break;
+ case 1:
+ EXPECT_EQ(RecordedOpId::RectOp, op.opId);
+ expectedMatrix.loadTranslate(-10, -10, 0);
+ EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix) << "Expect rounded offset";
+ break;
+ case 2:
+ EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
+ // Don't bother asserting recording state data - it's not used
+ break;
+ default:
+ ADD_FAILURE();
+ }
+ });
+ EXPECT_EQ(3, count);
+}
+
TEST(RecordingCanvas, saveLayer_missingRestore) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
@@ -494,6 +545,21 @@
EXPECT_EQ(3, count);
}
+TEST(RecordingCanvas, saveLayer_rejectBegin) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ canvas.save(SaveFlags::MatrixClip);
+ canvas.translate(0, -20); // avoid identity case
+ // empty clip rect should force layer + contents to be rejected
+ canvas.clipRect(0, -20, 200, -20, SkRegion::kIntersect_Op);
+ canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
+ canvas.drawRect(0, 0, 200, 200, SkPaint());
+ canvas.restore();
+ canvas.restore();
+ });
+
+ ASSERT_EQ(0u, dl->getOps().size()) << "Begin/Rect/End should all be rejected.";
+}
+
TEST(RecordingCanvas, drawRenderNode_rejection) {
auto child = TestUtils::createNode(50, 50, 150, 150,
[](RenderProperties& props, RecordingCanvas& canvas) {
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index b2997df..cf76a86 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -16,13 +16,26 @@
#include <gtest/gtest.h>
+#include "AnimationContext.h"
+#include "DamageAccumulator.h"
+#include "IContextFactory.h"
#include "RenderNode.h"
#include "TreeInfo.h"
+#include "renderthread/CanvasContext.h"
#include "tests/common/TestUtils.h"
#include "utils/Color.h"
using namespace android;
using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+
+class ContextFactory : public android::uirenderer::IContextFactory {
+public:
+ android::uirenderer::AnimationContext* createAnimationContext
+ (android::uirenderer::renderthread::TimeLord& clock) override {
+ return new android::uirenderer::AnimationContext(clock);
+ }
+};
TEST(RenderNode, hasParents) {
auto child = TestUtils::createNode(0, 0, 200, 400,
@@ -89,3 +102,31 @@
TestUtils::syncHierarchyPropertiesAndDisplayList(node);
EXPECT_EQ(0, refcnt);
}
+
+RENDERTHREAD_TEST(RenderNode, prepareTree_nullableDisplayList) {
+ ContextFactory contextFactory;
+ CanvasContext canvasContext(renderThread, false, nullptr, &contextFactory);
+ TreeInfo info(TreeInfo::MODE_RT_ONLY, canvasContext);
+ DamageAccumulator damageAccumulator;
+ info.damageAccumulator = &damageAccumulator;
+ info.observer = nullptr;
+
+ {
+ auto nonNullDLNode = TestUtils::createNode(0, 0, 200, 400,
+ [](RenderProperties& props, TestCanvas& canvas) {
+ canvas.drawColor(Color::Red_500, SkXfermode::kSrcOver_Mode);
+ });
+ TestUtils::syncHierarchyPropertiesAndDisplayList(nonNullDLNode);
+ EXPECT_TRUE(nonNullDLNode->getDisplayList());
+ nonNullDLNode->prepareTree(info);
+ }
+
+ {
+ auto nullDLNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
+ TestUtils::syncHierarchyPropertiesAndDisplayList(nullDLNode);
+ EXPECT_FALSE(nullDLNode->getDisplayList());
+ nullDLNode->prepareTree(info);
+ }
+
+ canvasContext.destroy(nullptr);
+}
diff --git a/libs/hwui/utils/TimeUtils.h b/libs/hwui/utils/TimeUtils.h
index 8d42d7e..ce181b7 100644
--- a/libs/hwui/utils/TimeUtils.h
+++ b/libs/hwui/utils/TimeUtils.h
@@ -21,10 +21,18 @@
namespace android {
namespace uirenderer {
+constexpr nsecs_t operator"" _s (unsigned long long s) {
+ return seconds_to_nanoseconds(s);
+}
+
constexpr nsecs_t operator"" _ms (unsigned long long ms) {
return milliseconds_to_nanoseconds(ms);
}
+constexpr nsecs_t operator"" _us (unsigned long long us) {
+ return microseconds_to_nanoseconds(us);
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/location/java/android/location/GnssMeasurementsEvent.java b/location/java/android/location/GnssMeasurementsEvent.java
index 3151694..7db0466 100644
--- a/location/java/android/location/GnssMeasurementsEvent.java
+++ b/location/java/android/location/GnssMeasurementsEvent.java
@@ -98,13 +98,13 @@
throw new InvalidParameterException("Parameter 'clock' must not be null.");
}
if (measurements == null || measurements.length == 0) {
- throw new InvalidParameterException(
- "Parameter 'measurements' must not be null or empty.");
+ mReadOnlyMeasurements = Collections.emptyList();
+ } else {
+ Collection<GnssMeasurement> measurementCollection = Arrays.asList(measurements);
+ mReadOnlyMeasurements = Collections.unmodifiableCollection(measurementCollection);
}
mClock = clock;
- Collection<GnssMeasurement> measurementCollection = Arrays.asList(measurements);
- mReadOnlyMeasurements = Collections.unmodifiableCollection(measurementCollection);
}
/**
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index a4484e7..81cc93d 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -263,6 +263,9 @@
* on some platforms when converting to short internally.
*/
public static final int ENCODING_IEC61937 = 13;
+ /** Audio data format: DOLBY TRUEHD compressed
+ **/
+ public static final int ENCODING_DOLBY_TRUEHD = 14;
/** Invalid audio channel configuration */
/** @deprecated Use {@link #CHANNEL_INVALID} instead. */
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index f19a262..33c1c3f 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3289,7 +3289,10 @@
/**
* Used as a key for {@link #getProperty} to request the native or optimal output sample rate
- * for this device's primary output stream, in decimal Hz.
+ * for this device's low latency output stream, in decimal Hz. Latency-sensitive apps
+ * should use this value as a default, and offer the user the option to override it.
+ * The low latency output stream is typically either the device's primary output stream,
+ * or another output stream with smaller buffers.
*/
// FIXME Deprecate
public static final String PROPERTY_OUTPUT_SAMPLE_RATE =
@@ -3297,7 +3300,10 @@
/**
* Used as a key for {@link #getProperty} to request the native or optimal output buffer size
- * for this device's primary output stream, in decimal PCM frames.
+ * for this device's low latency output stream, in decimal PCM frames. Latency-sensitive apps
+ * should use this value as a minimum, and offer the user the option to override it.
+ * The low latency output stream is typically either the device's primary output stream,
+ * or another output stream with smaller buffers.
*/
// FIXME Deprecate
public static final String PROPERTY_OUTPUT_FRAMES_PER_BUFFER =
diff --git a/media/java/android/media/AudioRecordingConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java
index 354339c..50dbd03 100644
--- a/media/java/android/media/AudioRecordingConfiguration.java
+++ b/media/java/android/media/AudioRecordingConfiguration.java
@@ -57,12 +57,12 @@
/**
* @hide
*/
- public AudioRecordingConfiguration(int session, int source, AudioFormat devFormat,
- AudioFormat clientFormat, int patchHandle) {
+ public AudioRecordingConfiguration(int session, int source, AudioFormat clientFormat,
+ AudioFormat devFormat, int patchHandle) {
mSessionId = session;
mClientSource = source;
- mDeviceFormat = devFormat;
mClientFormat = clientFormat;
+ mDeviceFormat = devFormat;
mPatchHandle = patchHandle;
}
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index ec2d4bc..2cbeb3a2 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -512,11 +512,36 @@
mAcquiredImages.clear();
nativeClose();
- }
- if (mEstimatedNativeAllocBytes > 0) {
- VMRuntime.getRuntime().registerNativeFree(mEstimatedNativeAllocBytes);
- mEstimatedNativeAllocBytes = 0;
+ if (mEstimatedNativeAllocBytes > 0) {
+ VMRuntime.getRuntime().registerNativeFree(mEstimatedNativeAllocBytes);
+ mEstimatedNativeAllocBytes = 0;
+ }
+ }
+ }
+
+ /**
+ * Discard any free buffers owned by this ImageReader.
+ *
+ * <p>
+ * Generally, the ImageReader caches buffers for reuse once they have been
+ * allocated, for best performance. However, sometimes it may be important to
+ * release all the cached, unused buffers to save on memory.
+ * </p>
+ * <p>
+ * Calling this method will discard all free cached buffers. This does not include any buffers
+ * associated with Images acquired from the ImageReader, any filled buffers waiting to be
+ * acquired, and any buffers currently in use by the source rendering buffers into the
+ * ImageReader's Surface.
+ * <p>
+ * The ImageReader continues to be usable after this call, but may need to reallocate buffers
+ * when more buffers are needed for rendering.
+ * </p>
+ * @hide
+ */
+ public void discardFreeBuffers() {
+ synchronized (mCloseLock) {
+ nativeDiscardFreeBuffers();
}
}
@@ -872,6 +897,7 @@
private synchronized native void nativeReleaseImage(Image i);
private synchronized native Surface nativeGetSurface();
private synchronized native int nativeDetachImage(Image i);
+ private synchronized native void nativeDiscardFreeBuffers();
/**
* @return A return code {@code ACQUIRE_*}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 542dced..264944f 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -133,9 +133,10 @@
<p class=note>
Note that on some devices the slice-height is advertised as 0. This could mean either that the
slice-height is the same as the frame height, or that the slice-height is the frame height
- aligned to some value (usually a power of 2). Unfortunately, there is no way to tell the actual
- slice height in this case. Furthermore, the vertical stride of the {@code U} plane in planar
- formats is also not specified or defined, though usually it is half of the slice height.
+ aligned to some value (usually a power of 2). Unfortunately, there is no standard and simple way
+ to tell the actual slice height in this case. Furthermore, the vertical stride of the {@code U}
+ plane in planar formats is also not specified or defined, though usually it is half of the slice
+ height.
<p>
The {@link MediaFormat#KEY_WIDTH} and {@link MediaFormat#KEY_HEIGHT} keys specify the size of the
video frames; however, for most encondings the video (picture) only occupies a portion of the
@@ -620,8 +621,9 @@
mode} will be automatically applied with one exception:
<p class=note>
Prior to the {@link android.os.Build.VERSION_CODES#M} release, software decoders may not
- have applied the rotation when being rendered onto a Surface. Unfortunately, there is no way to
- identify software decoders, or if they apply the rotation other than by trying it out.
+ have applied the rotation when being rendered onto a Surface. Unfortunately, there is no standard
+ and simple way to identify software decoders, or if they apply the rotation other than by trying
+ it out.
<p>
There are also some caveats.
<p class=note>
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index f7becf5..da490b9 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -69,8 +69,9 @@
// More video file types
public static final int FILE_TYPE_MP2PS = 200;
+ public static final int FILE_TYPE_QT = 201;
private static final int FIRST_VIDEO_FILE_TYPE2 = FILE_TYPE_MP2PS;
- private static final int LAST_VIDEO_FILE_TYPE2 = FILE_TYPE_MP2PS;
+ private static final int LAST_VIDEO_FILE_TYPE2 = FILE_TYPE_QT;
// Image file types
public static final int FILE_TYPE_JPEG = 31;
@@ -211,6 +212,8 @@
addFileType("MPG", FILE_TYPE_MP4, "video/mpeg", MtpConstants.FORMAT_MPEG);
addFileType("MP4", FILE_TYPE_MP4, "video/mp4", MtpConstants.FORMAT_MPEG);
addFileType("M4V", FILE_TYPE_M4V, "video/mp4", MtpConstants.FORMAT_MPEG);
+ addFileType("MOV", FILE_TYPE_QT, "video/quicktime", MtpConstants.FORMAT_MPEG);
+
addFileType("3GP", FILE_TYPE_3GPP, "video/3gpp", MtpConstants.FORMAT_3GP_CONTAINER);
addFileType("3GPP", FILE_TYPE_3GPP, "video/3gpp", MtpConstants.FORMAT_3GP_CONTAINER);
addFileType("3G2", FILE_TYPE_3GPP2, "video/3gpp2", MtpConstants.FORMAT_3GP_CONTAINER);
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index d7a18d9..d74aa81 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -49,12 +49,18 @@
* <tr><td>{@link #KEY_FRAME_RATE}</td><td>Integer or Float</td><td>required for <b>encoders</b>,
* optional for <b>decoders</b></td></tr>
* <tr><td>{@link #KEY_CAPTURE_RATE}</td><td>Integer</td><td></td></tr>
- * <tr><td>{@link #KEY_I_FRAME_INTERVAL}</td><td>Integer</td><td><b>encoder-only</b></td></tr>
+ * <tr><td>{@link #KEY_I_FRAME_INTERVAL}</td><td>Integer (or Float)</td><td><b>encoder-only</b>,
+ * time-interval between key frames.
+ * Float support added in {@link android.os.Build.VERSION_CODES#N_MR1}</td></tr>
* <tr><td>{@link #KEY_INTRA_REFRESH_PERIOD}</td><td>Integer</td><td><b>encoder-only</b>, optional</td></tr>
* <tr><td>{@link #KEY_MAX_WIDTH}</td><td>Integer</td><td><b>decoder-only</b>, optional, max-resolution width</td></tr>
* <tr><td>{@link #KEY_MAX_HEIGHT}</td><td>Integer</td><td><b>decoder-only</b>, optional, max-resolution height</td></tr>
- * <tr><td>{@link #KEY_REPEAT_PREVIOUS_FRAME_AFTER}</td><td>Long</td><td><b>video encoder in surface-mode only</b></td></tr>
- * <tr><td>{@link #KEY_PUSH_BLANK_BUFFERS_ON_STOP}</td><td>Integer(1)</td><td><b>video decoder rendering to a surface only</b></td></tr>
+ * <tr><td>{@link #KEY_REPEAT_PREVIOUS_FRAME_AFTER}</td><td>Long</td><td><b>encoder in surface-mode
+ * only</b>, optional</td></tr>
+ * <tr><td>{@link #KEY_PUSH_BLANK_BUFFERS_ON_STOP}</td><td>Integer(1)</td><td><b>decoder rendering
+ * to a surface only</b>, optional</td></tr>
+ * <tr><td>{@link #KEY_TEMPORAL_LAYERING}</td><td>String</td><td><b>encoder only</b>, optional,
+ * temporal-layering schema</td></tr>
* </table>
* Specify both {@link #KEY_MAX_WIDTH} and {@link #KEY_MAX_HEIGHT} to enable
* adaptive playback (seamless resolution change) for a video decoder that
@@ -258,9 +264,20 @@
public static final String KEY_CAPTURE_RATE = "capture-rate";
/**
- * A key describing the frequency of I frames expressed in secs
- * between I frames.
- * The associated value is an integer.
+ * A key describing the frequency of key frames expressed in seconds between key frames.
+ * <p>
+ * This key is used by video encoders.
+ * A negative value means no key frames are requested after the first frame.
+ * A zero value means a stream containing all key frames is requested.
+ * <p class=note>
+ * Most video encoders will convert this value of the number of non-key-frames between
+ * key-frames, using the {@linkplain #KEY_FRAME_RATE frame rate} information; therefore,
+ * if the actual frame rate differs (e.g. input frames are dropped or the frame rate
+ * changes), the <strong>time interval</strong> between key frames will not be the
+ * configured value.
+ * <p>
+ * The associated value is an integer (or float since
+ * {@link android.os.Build.VERSION_CODES#N_MR1}).
*/
public static final String KEY_I_FRAME_INTERVAL = "i-frame-interval";
@@ -280,12 +297,18 @@
/**
* A key describing the temporal layering schema. This is an optional parameter
- * that applies only to video encoders. Use {@link MediaCodec#getInputFormat}
+ * that applies only to video encoders. Use {@link MediaCodec#getOutputFormat}
* after {@link MediaCodec#configure configure} to query if the encoder supports
- * the desired schema. Supported values are {@code webrtc.vp8.1-layer},
- * {@code webrtc.vp8.2-layer}, {@code webrtc.vp8.3-layer}, and {@code none}.
- * If the encoder does not support temporal layering, the input format will
- * not have an entry with this key.
+ * the desired schema. Supported values are {@code webrtc.vp8.N-layer},
+ * {@code android.generic.N}, {@code android.generic.N+M} and {@code none}, where
+ * {@code N} denotes the total number of non-bidirectional layers (which must be at least 1)
+ * and {@code M} denotes the total number of bidirectional layers (which must be non-negative).
+ * <p class=note>{@code android.generic.*} schemas have been added in {@link
+ * android.os.Build.VERSION_CODES#N_MR1}.
+ * <p>
+ * The encoder may support fewer temporal layers, in which case the output format
+ * will contain the configured schema. If the encoder does not support temporal
+ * layering, the output format will not have an entry with this key.
* The associated value is a string.
*/
public static final String KEY_TEMPORAL_LAYERING = "ts-schema";
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 8d4a151..31c7a32 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -3498,7 +3498,7 @@
* @param extra an extra code, specific to the info. Typically
* implementation dependent.
* @return True if the method handled the info, false if it didn't.
- * Returning false, or not having an OnErrorListener at all, will
+ * Returning false, or not having an OnInfoListener at all, will
* cause the info to be discarded.
*/
boolean onInfo(MediaPlayer mp, int what, int extra);
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 73485af..94e894f 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -746,6 +746,27 @@
}
/**
+ * Sets the video encoding profile for recording. Call this method before prepare().
+ * Prepare() may perform additional checks on the parameter to make sure whether the
+ * specified profile and level are applicable, and sometimes the passed profile or
+ * level will be discarded due to codec capablity or to ensure the video recording
+ * can proceed smoothly based on the capabilities of the platform.
+ * @hide
+ * @param profile declared in {@link MediaCodecInfo.CodecProfileLevel}.
+ * @param level declared in {@link MediaCodecInfo.CodecProfileLevel}.
+ */
+ public void setVideoEncodingProfileLevel(int profile, int level) {
+ if (profile <= 0) {
+ throw new IllegalArgumentException("Video encoding profile is not positive");
+ }
+ if (level <= 0) {
+ throw new IllegalArgumentException("Video encoding level is not positive");
+ }
+ setParameter("video-param-encoder-profile=" + profile);
+ setParameter("video-param-encoder-level=" + level);
+ }
+
+ /**
* Currently not implemented. It does nothing.
* @deprecated Time lapse mode video recording using camera still image capture
* is not desirable, and will not be supported.
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 5fd85d1..c8ab5f9 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -60,11 +60,14 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.text.SimpleDateFormat;
+import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
+import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -451,6 +454,8 @@
private class MyMediaScannerClient implements MediaScannerClient {
+ private final SimpleDateFormat mDateFormatter;
+
private String mArtist;
private String mAlbumArtist; // use this if mArtist is missing
private String mAlbum;
@@ -463,6 +468,7 @@
private int mYear;
private int mDuration;
private String mPath;
+ private long mDate;
private long mLastModified;
private long mFileSize;
private String mWriter;
@@ -472,6 +478,11 @@
private int mWidth;
private int mHeight;
+ public MyMediaScannerClient() {
+ mDateFormatter = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
+ mDateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
public FileEntry beginFile(String path, String mimeType, long lastModified,
long fileSize, boolean isDirectory, boolean noMedia) {
mMimeType = mimeType;
@@ -537,6 +548,7 @@
mYear = 0;
mDuration = 0;
mPath = path;
+ mDate = 0;
mLastModified = lastModified;
mWriter = null;
mCompilation = 0;
@@ -627,6 +639,14 @@
return result;
}
+ private long parseDate(String date) {
+ try {
+ return mDateFormatter.parse(date).getTime();
+ } catch (ParseException e) {
+ return 0;
+ }
+ }
+
private int parseSubstring(String s, int start, int defaultValue) {
int length = s.length();
if (start == length) return defaultValue;
@@ -684,6 +704,8 @@
mCompilation = parseSubstring(value, 0, 0);
} else if (name.equalsIgnoreCase("isdrm")) {
mIsDrm = (parseSubstring(value, 0, 0) == 1);
+ } else if (name.equalsIgnoreCase("date")) {
+ mDate = parseDate(value);
} else if (name.equalsIgnoreCase("width")) {
mWidth = parseSubstring(value, 0, 0);
} else if (name.equalsIgnoreCase("height")) {
@@ -830,6 +852,9 @@
if (resolution != null) {
map.put(Video.Media.RESOLUTION, resolution);
}
+ if (mDate > 0) {
+ map.put(Video.Media.DATE_TAKEN, mDate);
+ }
} else if (MediaFile.isImageFileType(mFileType)) {
// FIXME - add DESCRIPTION
} else if (MediaFile.isAudioFileType(mFileType)) {
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index 0f7dc9a..b262d97 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -181,10 +181,15 @@
* @return
*/
boolean isRestricted_sync() {
+ // check app ops
+ if (mHasAppOpsPlayAudio) {
+ return false;
+ }
+ // check bypass flag
if ((mAttributes.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
return false;
}
- return !mHasAppOpsPlayAudio;
+ return true;
}
// Abstract methods a subclass needs to implement
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 5ede1d5..9fafda4 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -505,27 +505,31 @@
}
private boolean isRestricted() {
- IAudioService service = getService();
- boolean cameraSoundForced = false;
-
- try {
- cameraSoundForced = service.isCameraSoundForced();
- } catch (RemoteException e) {
- Log.e(TAG, "Cannot access AudioService in isRestricted()");
- }
-
- if (cameraSoundForced &&
- ((mAttributes.getAllFlags() & AudioAttributes.FLAG_AUDIBILITY_ENFORCED) != 0)
-// FIXME: should also check usage when set properly by camera app
-// && (mAttributes.getUsage() == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
- ) {
+ // check app ops
+ if (mHasAppOpsPlayAudio) {
return false;
}
-
+ // check bypass flag
if ((mAttributes.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
return false;
}
- return !mHasAppOpsPlayAudio;
+ // check force audibility flag and camera restriction
+ if ((mAttributes.getAllFlags() & AudioAttributes.FLAG_AUDIBILITY_ENFORCED) != 0) {
+// FIXME: should also check usage when set properly by camera app
+// && (mAttributes.getUsage() == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+ boolean cameraSoundForced = false;
+ try {
+ cameraSoundForced = getService().isCameraSoundForced();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Cannot access AudioService in isRestricted()");
+ } catch (NullPointerException e) {
+ Log.e(TAG, "Null AudioService in isRestricted()");
+ }
+ if (cameraSoundForced) {
+ return false;
+ }
+ }
+ return true;
}
private void updateAppOpsPlayAudio() {
diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetector.java b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
index df0961b..d5296ae 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerDetector.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
@@ -15,6 +15,7 @@
*/
package android.media.soundtrigger;
+import static android.hardware.soundtrigger.SoundTrigger.STATUS_OK;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -243,27 +244,29 @@
boolean allowMultipleTriggers =
(recognitionFlags & RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS) != 0;
+ int status = STATUS_OK;
try {
- mSoundTriggerService.startRecognition(new ParcelUuid(mSoundModelId),
+ status = mSoundTriggerService.startRecognition(new ParcelUuid(mSoundModelId),
mRecognitionCallback, new RecognitionConfig(captureTriggerAudio,
allowMultipleTriggers, null, null));
} catch (RemoteException e) {
return false;
}
- return true;
+ return status == STATUS_OK;
}
/**
* Stops recognition for the associated model.
*/
public boolean stopRecognition() {
+ int status = STATUS_OK;
try {
- mSoundTriggerService.stopRecognition(new ParcelUuid(mSoundModelId),
+ status = mSoundTriggerService.stopRecognition(new ParcelUuid(mSoundModelId),
mRecognitionCallback);
} catch (RemoteException e) {
return false;
}
- return true;
+ return status == STATUS_OK;
}
/**
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index c3993ae..724fc02 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -611,6 +611,23 @@
return OK;
}
+static void ImageReader_discardFreeBuffers(JNIEnv* env, jobject thiz) {
+ ALOGV("%s:", __FUNCTION__);
+ JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz);
+ if (ctx == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", "ImageReader was already closed");
+ return;
+ }
+
+ BufferItemConsumer* bufferConsumer = ctx->getBufferConsumer();
+ status_t res = bufferConsumer->discardFreeBuffers();
+ if (res != OK) {
+ ALOGE("Buffer discard failed: %s (%d)", strerror(-res), res);
+ jniThrowRuntimeException(env,
+ "nativeDicardFreebuffers failed");
+ }
+}
+
static jobject ImageReader_getSurface(JNIEnv* env, jobject thiz)
{
ALOGV("%s: ", __FUNCTION__);
@@ -773,6 +790,7 @@
{"nativeImageSetup", "(Landroid/media/Image;)I", (void*)ImageReader_imageSetup },
{"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface },
{"nativeDetachImage", "(Landroid/media/Image;)I", (void*)ImageReader_detachImage },
+ {"nativeDiscardFreeBuffers", "()V", (void*)ImageReader_discardFreeBuffers }
};
static const JNINativeMethod gImageMethods[] = {
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 38ed932..0f5dd3a 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -16,6 +16,13 @@
package android.opengl;
+import android.content.Context;
+import android.os.Trace;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
import java.io.Writer;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -29,15 +36,6 @@
import javax.microedition.khronos.opengles.GL;
import javax.microedition.khronos.opengles.GL10;
-import android.content.Context;
-import android.content.pm.ConfigurationInfo;
-import android.os.SystemProperties;
-import android.os.Trace;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-
/**
* An implementation of SurfaceView that uses the dedicated surface for
* displaying OpenGL rendering.
@@ -119,9 +117,9 @@
* {@link #setRenderMode}. The default is continuous rendering.
* <p>
* <h3>Activity Life-cycle</h3>
- * A GLSurfaceView must be notified when the activity is paused and resumed. GLSurfaceView clients
- * are required to call {@link #onPause()} when the activity pauses and
- * {@link #onResume()} when the activity resumes. These calls allow GLSurfaceView to
+ * A GLSurfaceView must be notified when to pause and resume rendering. GLSurfaceView clients
+ * are required to call {@link #onPause()} when the activity stops and
+ * {@link #onResume()} when the activity starts. These calls allow GLSurfaceView to
* pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate
* the OpenGL display.
* <p>
@@ -294,10 +292,12 @@
* resumed.
* <p>
* If set to true, then the EGL context may be preserved when the GLSurfaceView is paused.
- * Whether the EGL context is actually preserved or not depends upon whether the
- * Android device that the program is running on can support an arbitrary number of EGL
- * contexts or not. Devices that can only support a limited number of EGL contexts must
- * release the EGL context in order to allow multiple applications to share the GPU.
+ * <p>
+ * Prior to API level 11, whether the EGL context is actually preserved or not
+ * depends upon whether the Android device can support an arbitrary number of
+ * EGL contexts or not. Devices that can only support a limited number of EGL
+ * contexts must release the EGL context in order to allow multiple applications
+ * to share the GPU.
* <p>
* If set to false, the EGL context will be released when the GLSurfaceView is paused,
* and recreated when the GLSurfaceView is resumed.
@@ -554,9 +554,13 @@
/**
- * Inform the view that the activity is paused. The owner of this view must
- * call this method when the activity is paused. Calling this method will
- * pause the rendering thread.
+ * Pause the rendering thread, optionally tearing down the EGL context
+ * depending upon the value of {@link #setPreserveEGLContextOnPause(boolean)}.
+ *
+ * This method should be called when it is no longer desirable for the
+ * GLSurfaceView to continue rendering, such as in response to
+ * {@link android.app.Activity#onStop Activity.onStop}.
+ *
* Must not be called before a renderer has been set.
*/
public void onPause() {
@@ -564,10 +568,12 @@
}
/**
- * Inform the view that the activity is resumed. The owner of this view must
- * call this method when the activity is resumed. Calling this method will
- * recreate the OpenGL display and resume the rendering
- * thread.
+ * Resumes the rendering thread, re-creating the OpenGL context if necessary. It
+ * is the counterpart to {@link #onPause()}.
+ *
+ * This method should typically be called in
+ * {@link android.app.Activity#onStart Activity.onStart}.
+ *
* Must not be called before a renderer has been set.
*/
public void onResume() {
@@ -1354,7 +1360,7 @@
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
boolean preserveEglContextOnPause = view == null ?
false : view.mPreserveEGLContextOnPause;
- if (!preserveEglContextOnPause || sGLThreadManager.shouldReleaseEGLContextWhenPausing()) {
+ if (!preserveEglContextOnPause) {
stopEglContextLocked();
if (LOG_SURFACE) {
Log.i("GLThread", "releasing EGL context because paused tid=" + getId());
@@ -1362,16 +1368,6 @@
}
}
- // When pausing, optionally terminate EGL:
- if (pausing) {
- if (sGLThreadManager.shouldTerminateEGLWhenPausing()) {
- mEglHelper.finish();
- if (LOG_SURFACE) {
- Log.i("GLThread", "terminating EGL because paused tid=" + getId());
- }
- }
- }
-
// Have we lost the SurfaceView surface?
if ((! mHasSurface) && (! mWaitingForSurface)) {
if (LOG_SURFACE) {
@@ -1411,7 +1407,7 @@
if (! mHaveEglContext) {
if (askedToReleaseEglContext) {
askedToReleaseEglContext = false;
- } else if (sGLThreadManager.tryAcquireEglContextLocked(this)) {
+ } else {
try {
mEglHelper.start();
} catch (RuntimeException t) {
@@ -1506,7 +1502,6 @@
if (createGlInterface) {
gl = (GL10) mEglHelper.createGL();
- sGLThreadManager.checkGLDriver(gl);
createGlInterface = false;
}
@@ -1888,111 +1883,16 @@
Log.i("GLThread", "exiting tid=" + thread.getId());
}
thread.mExited = true;
- if (mEglOwner == thread) {
- mEglOwner = null;
- }
notifyAll();
}
/*
- * Tries once to acquire the right to use an EGL
- * context. Does not block. Requires that we are already
- * in the sGLThreadManager monitor when this is called.
- *
- * @return true if the right to use an EGL context was acquired.
- */
- public boolean tryAcquireEglContextLocked(GLThread thread) {
- if (mEglOwner == thread || mEglOwner == null) {
- mEglOwner = thread;
- notifyAll();
- return true;
- }
- checkGLESVersion();
- if (mMultipleGLESContextsAllowed) {
- return true;
- }
- // Notify the owning thread that it should release the context.
- // TODO: implement a fairness policy. Currently
- // if the owning thread is drawing continuously it will just
- // reacquire the EGL context.
- if (mEglOwner != null) {
- mEglOwner.requestReleaseEglContextLocked();
- }
- return false;
- }
-
- /*
* Releases the EGL context. Requires that we are already in the
* sGLThreadManager monitor when this is called.
*/
public void releaseEglContextLocked(GLThread thread) {
- if (mEglOwner == thread) {
- mEglOwner = null;
- }
notifyAll();
}
-
- public synchronized boolean shouldReleaseEGLContextWhenPausing() {
- // Release the EGL context when pausing even if
- // the hardware supports multiple EGL contexts.
- // Otherwise the device could run out of EGL contexts.
- return mLimitedGLESContexts;
- }
-
- public synchronized boolean shouldTerminateEGLWhenPausing() {
- checkGLESVersion();
- return !mMultipleGLESContextsAllowed;
- }
-
- public synchronized void checkGLDriver(GL10 gl) {
- if (! mGLESDriverCheckComplete) {
- checkGLESVersion();
- String renderer = gl.glGetString(GL10.GL_RENDERER);
- if (mGLESVersion < kGLES_20) {
- mMultipleGLESContextsAllowed =
- ! renderer.startsWith(kMSM7K_RENDERER_PREFIX);
- notifyAll();
- }
- mLimitedGLESContexts = !mMultipleGLESContextsAllowed;
- if (LOG_SURFACE) {
- Log.w(TAG, "checkGLDriver renderer = \"" + renderer + "\" multipleContextsAllowed = "
- + mMultipleGLESContextsAllowed
- + " mLimitedGLESContexts = " + mLimitedGLESContexts);
- }
- mGLESDriverCheckComplete = true;
- }
- }
-
- private void checkGLESVersion() {
- if (! mGLESVersionCheckComplete) {
- mGLESVersion = SystemProperties.getInt(
- "ro.opengles.version",
- ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
- if (mGLESVersion >= kGLES_20) {
- mMultipleGLESContextsAllowed = true;
- }
- if (LOG_SURFACE) {
- Log.w(TAG, "checkGLESVersion mGLESVersion =" +
- " " + mGLESVersion + " mMultipleGLESContextsAllowed = " + mMultipleGLESContextsAllowed);
- }
- mGLESVersionCheckComplete = true;
- }
- }
-
- /**
- * This check was required for some pre-Android-3.0 hardware. Android 3.0 provides
- * support for hardware-accelerated views, therefore multiple EGL contexts are
- * supported on all Android 3.0+ EGL drivers.
- */
- private boolean mGLESVersionCheckComplete;
- private int mGLESVersion;
- private boolean mGLESDriverCheckComplete;
- private boolean mMultipleGLESContextsAllowed;
- private boolean mLimitedGLESContexts;
- private static final int kGLES_20 = 0x20000;
- private static final String kMSM7K_RENDERER_PREFIX =
- "Q3Dimension MSM7500 ";
- private GLThread mEglOwner;
}
private static final GLThreadManager sGLThreadManager = new GLThreadManager();
diff --git a/packages/BackupRestoreConfirmation/res/values-fr/strings.xml b/packages/BackupRestoreConfirmation/res/values-fr/strings.xml
index b8d6b56..f40b02a 100644
--- a/packages/BackupRestoreConfirmation/res/values-fr/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-fr/strings.xml
@@ -18,10 +18,10 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="backup_confirm_title" msgid="827563724209303345">"Sauvegarde complète"</string>
<string name="restore_confirm_title" msgid="5469365809567486602">"Restauration complète"</string>
- <string name="backup_confirm_text" msgid="1878021282758896593">"Vous avez demandé une sauvegarde complète de l\'ensemble des données vers un ordinateur de bureau connecté. Voulez-vous l\'autoriser ?\n\nSi vous n\'avez pas demandé la sauvegarde vous-même, n\'autorisez pas la poursuite de l\'opération."</string>
+ <string name="backup_confirm_text" msgid="1878021282758896593">"Vous avez demandé une sauvegarde complète de l\'ensemble des données vers un ordinateur connecté. Voulez-vous l\'autoriser ?\n\nSi vous n\'avez pas demandé la sauvegarde vous-même, n\'autorisez pas la poursuite de l\'opération."</string>
<string name="allow_backup_button_label" msgid="4217228747769644068">"Sauvegarder mes données"</string>
<string name="deny_backup_button_label" msgid="6009119115581097708">"Ne pas sauvegarder"</string>
- <string name="restore_confirm_text" msgid="7499866728030461776">"Vous avez demandé une restauration complète de l\'ensemble des données à partir d\'un ordinateur de bureau connecté. Voulez-vous l\'autoriser ?\n\nSi vous n\'avez pas demandé vous-même la restauration, n\'autorisez pas sa poursuite. Cette opération remplacera toutes les données actuellement sur l\'appareil !"</string>
+ <string name="restore_confirm_text" msgid="7499866728030461776">"Vous avez demandé une restauration complète de l\'ensemble des données à partir d\'un ordinateur connecté. Voulez-vous l\'autoriser ?\n\nSi vous n\'avez pas demandé vous-même la restauration, n\'autorisez pas sa poursuite. Cette opération remplacera toutes les données actuellement sur l\'appareil !"</string>
<string name="allow_restore_button_label" msgid="3081286752277127827">"Restaurer mes données"</string>
<string name="deny_restore_button_label" msgid="1724367334453104378">"Ne pas restaurer"</string>
<string name="current_password_text" msgid="8268189555578298067">"Veuillez saisir votre mot de passe de sauvegarde actuel ci-dessous :"</string>
diff --git a/packages/BackupRestoreConfirmation/res/values-my-rMM/strings.xml b/packages/BackupRestoreConfirmation/res/values-my-rMM/strings.xml
index d499771..9134649 100644
--- a/packages/BackupRestoreConfirmation/res/values-my-rMM/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-my-rMM/strings.xml
@@ -26,7 +26,7 @@
<string name="deny_restore_button_label" msgid="1724367334453104378">"ပြန်လည်ရယူခြင်းအား မပြုလုပ်ပါနှင့်"</string>
<string name="current_password_text" msgid="8268189555578298067">"သင့်လက်ရှိ အရံသိမ်းဆည်းမှု လျှို့ဝှက်စကားဝှက်အား ထည့်သွင်းပါ။"</string>
<string name="device_encryption_restore_text" msgid="1570864916855208992">"သင့်စက်၏ လျှို့ဝှက်အသွင်ပြောင်းခြင်းအတွက်စကားဝှက်ကို ထည့်သွင်းပါ။"</string>
- <string name="device_encryption_backup_text" msgid="5866590762672844664">"သင့်စက်၏လျှို့ဝှက်အသွင်ပြောင်းခြင်းအတွက် လျှို့ဝှက်စကားဝှက်အားထည့်ပါ။ အရံသိမ်းဆည်းမှု သိမ်းဆည်းနေရာတွင်လည်း အသုံးပြုမည်ဖြစ်သည်။"</string>
+ <string name="device_encryption_backup_text" msgid="5866590762672844664">"သင့်စက်၏အသွင်ပြောင်းခြင်းအတွက် စကားဝှက်ကို ထည့်ပါ။ အရန်မှတ်တမ်းတွင်လည်း အသုံးပြုပါမည်။"</string>
<string name="backup_enc_password_text" msgid="4981585714795233099">"ဒေတာအားလုံးအားအရန်သိမ်းဆည်းခြင်းပြီးလျှို့ဝှက်အသွင်ပြောင်းခြင်းအတွက် လျှို့ဝှက်နံပါတ်/စာကိုထည့်ပါ။ အကယ်၍ ကွက်လပ်ထားပါက ယခုသင့်လက်ရှိလျှို့ဝှက်စကားဝှက်အား အသုံးပြုပါမည်။"</string>
<string name="backup_enc_password_optional" msgid="1350137345907579306">"အကယ်၍ ဒေတာအားလုံးအားအရန်သိမ်းဆည်းခြင်းကို ဝှက်လိုပါက အောက်တွင်လျှို့ဝှက်နံပါတ်/စာကိုထည့်ပါ။"</string>
<string name="backup_enc_password_required" msgid="7889652203371654149">"သင်၏ ကိရိယာကို လျှို့ဝျက်ကုဒ် သွင်းထားရာ၊ သင်သည် သင်၏ ဘက်အာပ်ကိုပါ လျှို့ဝျက်ကုဒ် သွင်းရန် လိုအပ်သည်။ ကျေးဇူးပြုပြီး အောက်မှာ စကားဝှက်ကို ထည့်သွင်းပါ:"</string>
diff --git a/packages/BackupRestoreConfirmation/res/values-ro/strings.xml b/packages/BackupRestoreConfirmation/res/values-ro/strings.xml
index 966e7f9..6a7c266 100644
--- a/packages/BackupRestoreConfirmation/res/values-ro/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-ro/strings.xml
@@ -19,7 +19,7 @@
<string name="backup_confirm_title" msgid="827563724209303345">"Copiere de rezervă completă"</string>
<string name="restore_confirm_title" msgid="5469365809567486602">"Restabilire completă"</string>
<string name="backup_confirm_text" msgid="1878021282758896593">"S-a solicitat crearea unei copii de rezervă complete a tuturor datelor pe un computer desktop conectat. Doriți să permiteți acest lucru?\n\nDacă nu ați solicitat dvs. copierea de rezervă, nu permiteți ca operațiunea să continue."</string>
- <string name="allow_backup_button_label" msgid="4217228747769644068">"Creați copii de rezervă pentru datele dvs."</string>
+ <string name="allow_backup_button_label" msgid="4217228747769644068">"Faceți backup pentru date"</string>
<string name="deny_backup_button_label" msgid="6009119115581097708">"Nu creați copii de rezervă"</string>
<string name="restore_confirm_text" msgid="7499866728030461776">"S-a solicitat o restabilire completă a tuturor datelor de pe un computer desktop conectat. Doriți să permiteți acest lucru?\n\nDacă nu dvs. ați solicitat această restabilire, nu permiteți continuarea operațiunii. Acest proces va înlocui toate datele existente în prezent pe dispozitiv!"</string>
<string name="allow_restore_button_label" msgid="3081286752277127827">"Restabiliți datele dvs."</string>
diff --git a/packages/CaptivePortalLogin/res/values-my-rMM/strings.xml b/packages/CaptivePortalLogin/res/values-my-rMM/strings.xml
index 41d3d79..e25570d 100644
--- a/packages/CaptivePortalLogin/res/values-my-rMM/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-my-rMM/strings.xml
@@ -7,5 +7,5 @@
<string name="action_bar_label" msgid="917235635415966620">"ကွန်ယက်သို့ လက်မှတ်ထိုးဝင်ရန်"</string>
<string name="ssl_error_warning" msgid="6653188881418638872">"သင်ချိတ်ဆက်ရန် ကြိုးစားနေသည့် ကွန်ရက်သည် လုံခြုံရေးပြဿနာ ရှိနေသည်။"</string>
<string name="ssl_error_example" msgid="647898534624078900">"ဥပမာ၊ ဝင်ရောက်ရန် စာမျက်နှာသည် ပြသထားသည့် အဖွဲ့အစည်းနှင့် သက်ဆိုင်မှု မရှိနိုင်ပါ။"</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"ဘရောက်ဇာမှတစ်ဆင့် ရှေ့ဆက်ရန်"</string>
+ <string name="ssl_error_continue" msgid="6492718244923937110">"ဘရောက်ဇာမှတစ်ဆင့် ဆက်လုပ်ရန်"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 6fb8b51..b58c87a 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -96,7 +96,7 @@
// Exit app if Network disappears.
final NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(mNetwork);
if (networkCapabilities == null) {
- finish();
+ finishAndRemoveTask();
return;
}
mNetworkCallback = new NetworkCallback() {
@@ -163,7 +163,7 @@
mCaptivePortal.useNetwork();
break;
}
- finish();
+ finishAndRemoveTask();
}
@Override
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 69912ab..a3edae3 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -11,6 +11,7 @@
<application
android:name=".DocumentsApplication"
android:label="@string/app_label"
+ android:icon="@mipmap/ic_app_icon"
android:supportsRtl="true">
<activity
diff --git a/packages/DocumentsUI/res/drawable/ic_check_circle.xml b/packages/DocumentsUI/res/drawable/ic_check_circle.xml
index d49ba6a..62a4e34 100644
--- a/packages/DocumentsUI/res/drawable/ic_check_circle.xml
+++ b/packages/DocumentsUI/res/drawable/ic_check_circle.xml
@@ -19,6 +19,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
- android:fillColor="#FF009688"
+ android:fillColor="?android:attr/colorAccent"
android:pathData="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10,-4.48 10,-10S17.52 2 12 2zm-2 15l-5,-5 1.41,-1.41L10 14.17l7.59,-7.59L19 8l-9 9z"/>
</vector>
diff --git a/packages/DocumentsUI/res/mipmap-hdpi/ic_app_icon.png b/packages/DocumentsUI/res/mipmap-hdpi/ic_app_icon.png
new file mode 100644
index 0000000..cd3a037
--- /dev/null
+++ b/packages/DocumentsUI/res/mipmap-hdpi/ic_app_icon.png
Binary files differ
diff --git a/packages/DocumentsUI/res/mipmap-mdpi/ic_app_icon.png b/packages/DocumentsUI/res/mipmap-mdpi/ic_app_icon.png
new file mode 100644
index 0000000..8d08e9b
--- /dev/null
+++ b/packages/DocumentsUI/res/mipmap-mdpi/ic_app_icon.png
Binary files differ
diff --git a/packages/DocumentsUI/res/mipmap-xhdpi/ic_app_icon.png b/packages/DocumentsUI/res/mipmap-xhdpi/ic_app_icon.png
new file mode 100644
index 0000000..f3bacb7
--- /dev/null
+++ b/packages/DocumentsUI/res/mipmap-xhdpi/ic_app_icon.png
Binary files differ
diff --git a/packages/DocumentsUI/res/mipmap-xxhdpi/ic_app_icon.png b/packages/DocumentsUI/res/mipmap-xxhdpi/ic_app_icon.png
new file mode 100644
index 0000000..5156171
--- /dev/null
+++ b/packages/DocumentsUI/res/mipmap-xxhdpi/ic_app_icon.png
Binary files differ
diff --git a/packages/DocumentsUI/res/mipmap-xxxhdpi/ic_app_icon.png b/packages/DocumentsUI/res/mipmap-xxxhdpi/ic_app_icon.png
new file mode 100644
index 0000000..6dc2f76
--- /dev/null
+++ b/packages/DocumentsUI/res/mipmap-xxxhdpi/ic_app_icon.png
Binary files differ
diff --git a/packages/DocumentsUI/res/values-af/strings.xml b/packages/DocumentsUI/res/values-af/strings.xml
index c4a5eeab..e3098a2 100644
--- a/packages/DocumentsUI/res/values-af/strings.xml
+++ b/packages/DocumentsUI/res/values-af/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokumente"</string>
+ <string name="app_label" msgid="1551050262492398204">"Lêers"</string>
<string name="downloads_label" msgid="959113951084633612">"Aflaaie"</string>
<string name="title_open" msgid="4353228937663917801">"Maak oop vanuit"</string>
<string name="title_save" msgid="2433679664882857999">"Stoor na"</string>
diff --git a/packages/DocumentsUI/res/values-am/strings.xml b/packages/DocumentsUI/res/values-am/strings.xml
index c3db723..84f11f5 100644
--- a/packages/DocumentsUI/res/values-am/strings.xml
+++ b/packages/DocumentsUI/res/values-am/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"ሰነዶች"</string>
+ <string name="app_label" msgid="1551050262492398204">"ፋይሎች"</string>
<string name="downloads_label" msgid="959113951084633612">"የወረዱ"</string>
<string name="title_open" msgid="4353228937663917801">"ክፈት ከ"</string>
<string name="title_save" msgid="2433679664882857999">"አስቀምጥ ወደ"</string>
diff --git a/packages/DocumentsUI/res/values-ar/strings.xml b/packages/DocumentsUI/res/values-ar/strings.xml
index 2b122a2..6fee984 100644
--- a/packages/DocumentsUI/res/values-ar/strings.xml
+++ b/packages/DocumentsUI/res/values-ar/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"مستندات"</string>
+ <string name="app_label" msgid="1551050262492398204">"الملفات"</string>
<string name="downloads_label" msgid="959113951084633612">"التنزيلات"</string>
<string name="title_open" msgid="4353228937663917801">"فتح من"</string>
<string name="title_save" msgid="2433679664882857999">"حفظ في"</string>
diff --git a/packages/DocumentsUI/res/values-az-rAZ/strings.xml b/packages/DocumentsUI/res/values-az-rAZ/strings.xml
index dfdc71a..56108a1 100644
--- a/packages/DocumentsUI/res/values-az-rAZ/strings.xml
+++ b/packages/DocumentsUI/res/values-az-rAZ/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Sənədlər"</string>
+ <string name="app_label" msgid="1551050262492398204">"Fayllar"</string>
<string name="downloads_label" msgid="959113951084633612">"Endirmələr"</string>
<string name="title_open" msgid="4353228937663917801">"Vasitəsilə açın"</string>
<string name="title_save" msgid="2433679664882857999">"buraya saxlayın"</string>
diff --git a/packages/DocumentsUI/res/values-b+sr+Latn/strings.xml b/packages/DocumentsUI/res/values-b+sr+Latn/strings.xml
index 23e375d..75b0d86 100644
--- a/packages/DocumentsUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/DocumentsUI/res/values-b+sr+Latn/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokumenti"</string>
+ <string name="app_label" msgid="1551050262492398204">"Datoteke"</string>
<string name="downloads_label" msgid="959113951084633612">"Preuzimanja"</string>
<string name="title_open" msgid="4353228937663917801">"Otvori sa"</string>
<string name="title_save" msgid="2433679664882857999">"Sačuvaj u"</string>
diff --git a/packages/DocumentsUI/res/values-be-rBY/strings.xml b/packages/DocumentsUI/res/values-be-rBY/strings.xml
index fabcdc2..91b8a63 100644
--- a/packages/DocumentsUI/res/values-be-rBY/strings.xml
+++ b/packages/DocumentsUI/res/values-be-rBY/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Дакументы"</string>
+ <string name="app_label" msgid="1551050262492398204">"Файлы"</string>
<string name="downloads_label" msgid="959113951084633612">"Спампоўкі"</string>
<string name="title_open" msgid="4353228937663917801">"Адкрыць з"</string>
<string name="title_save" msgid="2433679664882857999">"Захаваць у"</string>
diff --git a/packages/DocumentsUI/res/values-bg/strings.xml b/packages/DocumentsUI/res/values-bg/strings.xml
index e068a10..8b5fdd8 100644
--- a/packages/DocumentsUI/res/values-bg/strings.xml
+++ b/packages/DocumentsUI/res/values-bg/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Документи"</string>
+ <string name="app_label" msgid="1551050262492398204">"Файлове"</string>
<string name="downloads_label" msgid="959113951084633612">"Изтегляния"</string>
<string name="title_open" msgid="4353228937663917801">"Отваряне от"</string>
<string name="title_save" msgid="2433679664882857999">"Запазване във:"</string>
diff --git a/packages/DocumentsUI/res/values-bn-rBD/strings.xml b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
index 1bdc204..7d4692d 100644
--- a/packages/DocumentsUI/res/values-bn-rBD/strings.xml
+++ b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"দস্তাবেজগুলি"</string>
+ <string name="app_label" msgid="1551050262492398204">"ফাইল"</string>
<string name="downloads_label" msgid="959113951084633612">"ডাউনলোডগুলি"</string>
<string name="title_open" msgid="4353228937663917801">"এখান থেকে খুলুন"</string>
<string name="title_save" msgid="2433679664882857999">"এতে সংরক্ষণ করুন"</string>
diff --git a/packages/DocumentsUI/res/values-bs-rBA/strings.xml b/packages/DocumentsUI/res/values-bs-rBA/strings.xml
index eff2744..42c8e9e 100644
--- a/packages/DocumentsUI/res/values-bs-rBA/strings.xml
+++ b/packages/DocumentsUI/res/values-bs-rBA/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokumenti"</string>
+ <string name="app_label" msgid="1551050262492398204">"Fajlovi"</string>
<string name="downloads_label" msgid="959113951084633612">"Preuzimanja"</string>
<string name="title_open" msgid="4353228937663917801">"Otvori iz"</string>
<string name="title_save" msgid="2433679664882857999">"Sačuvaj u"</string>
diff --git a/packages/DocumentsUI/res/values-ca/strings.xml b/packages/DocumentsUI/res/values-ca/strings.xml
index 3436ed4..e25731d 100644
--- a/packages/DocumentsUI/res/values-ca/strings.xml
+++ b/packages/DocumentsUI/res/values-ca/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Documents"</string>
+ <string name="app_label" msgid="1551050262492398204">"Fitxers"</string>
<string name="downloads_label" msgid="959113951084633612">"Baixades"</string>
<string name="title_open" msgid="4353228937663917801">"Obre des de"</string>
<string name="title_save" msgid="2433679664882857999">"Desa a"</string>
diff --git a/packages/DocumentsUI/res/values-cs/strings.xml b/packages/DocumentsUI/res/values-cs/strings.xml
index 90d30bc..190f60e 100644
--- a/packages/DocumentsUI/res/values-cs/strings.xml
+++ b/packages/DocumentsUI/res/values-cs/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokumenty"</string>
+ <string name="app_label" msgid="1551050262492398204">"Soubory"</string>
<string name="downloads_label" msgid="959113951084633612">"Stahování"</string>
<string name="title_open" msgid="4353228937663917801">"Otevřít"</string>
<string name="title_save" msgid="2433679664882857999">"Uložit do"</string>
diff --git a/packages/DocumentsUI/res/values-da/strings.xml b/packages/DocumentsUI/res/values-da/strings.xml
index 4900cd0..73350d7 100644
--- a/packages/DocumentsUI/res/values-da/strings.xml
+++ b/packages/DocumentsUI/res/values-da/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokumenter"</string>
+ <string name="app_label" msgid="1551050262492398204">"Filer"</string>
<string name="downloads_label" msgid="959113951084633612">"Downloads"</string>
<string name="title_open" msgid="4353228937663917801">"Åbn fra"</string>
<string name="title_save" msgid="2433679664882857999">"Gem i"</string>
diff --git a/packages/DocumentsUI/res/values-de/strings.xml b/packages/DocumentsUI/res/values-de/strings.xml
index 4cd2527..21a298f 100644
--- a/packages/DocumentsUI/res/values-de/strings.xml
+++ b/packages/DocumentsUI/res/values-de/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokumente"</string>
+ <string name="app_label" msgid="1551050262492398204">"Dateien"</string>
<string name="downloads_label" msgid="959113951084633612">"Downloads"</string>
<string name="title_open" msgid="4353228937663917801">"Öffnen von"</string>
<string name="title_save" msgid="2433679664882857999">"Speichern unter"</string>
diff --git a/packages/DocumentsUI/res/values-el/strings.xml b/packages/DocumentsUI/res/values-el/strings.xml
index 461b191..afa4d89 100644
--- a/packages/DocumentsUI/res/values-el/strings.xml
+++ b/packages/DocumentsUI/res/values-el/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Έγγραφα"</string>
+ <string name="app_label" msgid="1551050262492398204">"Αρχεία"</string>
<string name="downloads_label" msgid="959113951084633612">"Λήψεις"</string>
<string name="title_open" msgid="4353228937663917801">"Άνοιγμα από"</string>
<string name="title_save" msgid="2433679664882857999">"Αποθήκευση σε"</string>
diff --git a/packages/DocumentsUI/res/values-en-rAU/strings.xml b/packages/DocumentsUI/res/values-en-rAU/strings.xml
index e062d20..c743e88 100644
--- a/packages/DocumentsUI/res/values-en-rAU/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rAU/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Documents"</string>
+ <string name="app_label" msgid="1551050262492398204">"Files"</string>
<string name="downloads_label" msgid="959113951084633612">"Downloads"</string>
<string name="title_open" msgid="4353228937663917801">"Open from"</string>
<string name="title_save" msgid="2433679664882857999">"Save to"</string>
@@ -107,7 +107,7 @@
<item quantity="one">Copied <xliff:g id="COUNT_0">%1$d</xliff:g> file to clipboard.</item>
</plurals>
<string name="clipboard_files_cannot_paste" msgid="2878324825602325706">"Cannot paste the selected files in this location."</string>
- <string name="menu_rename" msgid="7678802479104285353">"rename"</string>
+ <string name="menu_rename" msgid="7678802479104285353">"Rename"</string>
<string name="rename_error" msgid="4203041674883412606">"Failed to rename document"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Some files were converted"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Grant <xliff:g id="APPNAME"><b>^1</b></xliff:g> access to <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> directory on <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
diff --git a/packages/DocumentsUI/res/values-en-rGB/strings.xml b/packages/DocumentsUI/res/values-en-rGB/strings.xml
index e062d20..c743e88 100644
--- a/packages/DocumentsUI/res/values-en-rGB/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rGB/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Documents"</string>
+ <string name="app_label" msgid="1551050262492398204">"Files"</string>
<string name="downloads_label" msgid="959113951084633612">"Downloads"</string>
<string name="title_open" msgid="4353228937663917801">"Open from"</string>
<string name="title_save" msgid="2433679664882857999">"Save to"</string>
@@ -107,7 +107,7 @@
<item quantity="one">Copied <xliff:g id="COUNT_0">%1$d</xliff:g> file to clipboard.</item>
</plurals>
<string name="clipboard_files_cannot_paste" msgid="2878324825602325706">"Cannot paste the selected files in this location."</string>
- <string name="menu_rename" msgid="7678802479104285353">"rename"</string>
+ <string name="menu_rename" msgid="7678802479104285353">"Rename"</string>
<string name="rename_error" msgid="4203041674883412606">"Failed to rename document"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Some files were converted"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Grant <xliff:g id="APPNAME"><b>^1</b></xliff:g> access to <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> directory on <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
diff --git a/packages/DocumentsUI/res/values-en-rIN/strings.xml b/packages/DocumentsUI/res/values-en-rIN/strings.xml
index e062d20..c743e88 100644
--- a/packages/DocumentsUI/res/values-en-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rIN/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Documents"</string>
+ <string name="app_label" msgid="1551050262492398204">"Files"</string>
<string name="downloads_label" msgid="959113951084633612">"Downloads"</string>
<string name="title_open" msgid="4353228937663917801">"Open from"</string>
<string name="title_save" msgid="2433679664882857999">"Save to"</string>
@@ -107,7 +107,7 @@
<item quantity="one">Copied <xliff:g id="COUNT_0">%1$d</xliff:g> file to clipboard.</item>
</plurals>
<string name="clipboard_files_cannot_paste" msgid="2878324825602325706">"Cannot paste the selected files in this location."</string>
- <string name="menu_rename" msgid="7678802479104285353">"rename"</string>
+ <string name="menu_rename" msgid="7678802479104285353">"Rename"</string>
<string name="rename_error" msgid="4203041674883412606">"Failed to rename document"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Some files were converted"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Grant <xliff:g id="APPNAME"><b>^1</b></xliff:g> access to <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> directory on <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
diff --git a/packages/DocumentsUI/res/values-es-rUS/strings.xml b/packages/DocumentsUI/res/values-es-rUS/strings.xml
index cbb148f..4007db2 100644
--- a/packages/DocumentsUI/res/values-es-rUS/strings.xml
+++ b/packages/DocumentsUI/res/values-es-rUS/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Documentos"</string>
+ <string name="app_label" msgid="1551050262492398204">"Archivos"</string>
<string name="downloads_label" msgid="959113951084633612">"Descargas"</string>
<string name="title_open" msgid="4353228937663917801">"Abrir desde"</string>
<string name="title_save" msgid="2433679664882857999">"Guardar en"</string>
diff --git a/packages/DocumentsUI/res/values-es/strings.xml b/packages/DocumentsUI/res/values-es/strings.xml
index 1efe13f..3eb14a7 100644
--- a/packages/DocumentsUI/res/values-es/strings.xml
+++ b/packages/DocumentsUI/res/values-es/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Documentos"</string>
+ <string name="app_label" msgid="1551050262492398204">"Archivos"</string>
<string name="downloads_label" msgid="959113951084633612">"Descargas"</string>
<string name="title_open" msgid="4353228937663917801">"Abrir desde"</string>
<string name="title_save" msgid="2433679664882857999">"Guardar en"</string>
diff --git a/packages/DocumentsUI/res/values-et-rEE/strings.xml b/packages/DocumentsUI/res/values-et-rEE/strings.xml
index ad1937f..ea8b1e5 100644
--- a/packages/DocumentsUI/res/values-et-rEE/strings.xml
+++ b/packages/DocumentsUI/res/values-et-rEE/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokumendid"</string>
+ <string name="app_label" msgid="1551050262492398204">"Failid"</string>
<string name="downloads_label" msgid="959113951084633612">"Allalaadimised"</string>
<string name="title_open" msgid="4353228937663917801">"Ava asukohast:"</string>
<string name="title_save" msgid="2433679664882857999">"Salvesta:"</string>
diff --git a/packages/DocumentsUI/res/values-eu-rES/strings.xml b/packages/DocumentsUI/res/values-eu-rES/strings.xml
index 28b8178..e2214be 100644
--- a/packages/DocumentsUI/res/values-eu-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-eu-rES/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokumentuak"</string>
+ <string name="app_label" msgid="1551050262492398204">"Fitxategiak"</string>
<string name="downloads_label" msgid="959113951084633612">"Deskargak"</string>
<string name="title_open" msgid="4353228937663917801">"Ireki hemendik"</string>
<string name="title_save" msgid="2433679664882857999">"Gorde hemen"</string>
diff --git a/packages/DocumentsUI/res/values-fa/strings.xml b/packages/DocumentsUI/res/values-fa/strings.xml
index b5158e2..f2f59ed 100644
--- a/packages/DocumentsUI/res/values-fa/strings.xml
+++ b/packages/DocumentsUI/res/values-fa/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"اسناد"</string>
+ <string name="app_label" msgid="1551050262492398204">"Files"</string>
<string name="downloads_label" msgid="959113951084633612">"بارگیریها"</string>
<string name="title_open" msgid="4353228937663917801">"باز کردن از"</string>
<string name="title_save" msgid="2433679664882857999">"ذخیره در"</string>
diff --git a/packages/DocumentsUI/res/values-fi/strings.xml b/packages/DocumentsUI/res/values-fi/strings.xml
index 2a27dde..32cde21 100644
--- a/packages/DocumentsUI/res/values-fi/strings.xml
+++ b/packages/DocumentsUI/res/values-fi/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Asiakirjat"</string>
+ <string name="app_label" msgid="1551050262492398204">"Tiedostot"</string>
<string name="downloads_label" msgid="959113951084633612">"Lataukset"</string>
<string name="title_open" msgid="4353228937663917801">"Avaa sijainnista"</string>
<string name="title_save" msgid="2433679664882857999">"Tallenna kohteeseen"</string>
diff --git a/packages/DocumentsUI/res/values-fr-rCA/strings.xml b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
index 9583b8a3..54a5c0a 100644
--- a/packages/DocumentsUI/res/values-fr-rCA/strings.xml
+++ b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Documents"</string>
+ <string name="app_label" msgid="1551050262492398204">"Fichiers"</string>
<string name="downloads_label" msgid="959113951084633612">"Téléchargements"</string>
<string name="title_open" msgid="4353228937663917801">"Ouvrir à partir de"</string>
<string name="title_save" msgid="2433679664882857999">"Enregistrer dans"</string>
diff --git a/packages/DocumentsUI/res/values-fr/strings.xml b/packages/DocumentsUI/res/values-fr/strings.xml
index 630a492..20292b6 100644
--- a/packages/DocumentsUI/res/values-fr/strings.xml
+++ b/packages/DocumentsUI/res/values-fr/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Docs"</string>
+ <string name="app_label" msgid="1551050262492398204">"Fichiers"</string>
<string name="downloads_label" msgid="959113951084633612">"Téléchargements"</string>
<string name="title_open" msgid="4353228937663917801">"Ouvrir à partir de"</string>
<string name="title_save" msgid="2433679664882857999">"Enregistrer sous"</string>
diff --git a/packages/DocumentsUI/res/values-gl-rES/strings.xml b/packages/DocumentsUI/res/values-gl-rES/strings.xml
index 47fc1c7..37197cf 100644
--- a/packages/DocumentsUI/res/values-gl-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-gl-rES/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Documentos"</string>
+ <string name="app_label" msgid="1551050262492398204">"Ficheiros"</string>
<string name="downloads_label" msgid="959113951084633612">"Descargas"</string>
<string name="title_open" msgid="4353228937663917801">"Abrir desde"</string>
<string name="title_save" msgid="2433679664882857999">"Gardar en"</string>
diff --git a/packages/DocumentsUI/res/values-gu-rIN/strings.xml b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
index 4d87d97..5b0db06 100644
--- a/packages/DocumentsUI/res/values-gu-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"દસ્તાવેજો"</string>
+ <string name="app_label" msgid="1551050262492398204">"ફાઇલો"</string>
<string name="downloads_label" msgid="959113951084633612">"ડાઉનલોડ્સ"</string>
<string name="title_open" msgid="4353228937663917801">"અહીંથી ખોલો"</string>
<string name="title_save" msgid="2433679664882857999">"આમાં સાચવો"</string>
diff --git a/packages/DocumentsUI/res/values-hi/strings.xml b/packages/DocumentsUI/res/values-hi/strings.xml
index 8420b02..04094f3 100644
--- a/packages/DocumentsUI/res/values-hi/strings.xml
+++ b/packages/DocumentsUI/res/values-hi/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"दस्तावेज़"</string>
+ <string name="app_label" msgid="1551050262492398204">"फ़ाइल"</string>
<string name="downloads_label" msgid="959113951084633612">"डाउनलोड"</string>
<string name="title_open" msgid="4353228937663917801">"यहां से खोलें"</string>
<string name="title_save" msgid="2433679664882857999">"यहां सहेजें"</string>
diff --git a/packages/DocumentsUI/res/values-hr/strings.xml b/packages/DocumentsUI/res/values-hr/strings.xml
index 8fe0a4d..15fb934 100644
--- a/packages/DocumentsUI/res/values-hr/strings.xml
+++ b/packages/DocumentsUI/res/values-hr/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokumenti"</string>
+ <string name="app_label" msgid="1551050262492398204">"Datoteke"</string>
<string name="downloads_label" msgid="959113951084633612">"Preuzimanja"</string>
<string name="title_open" msgid="4353228937663917801">"Otvori iz"</string>
<string name="title_save" msgid="2433679664882857999">"Spremi u"</string>
diff --git a/packages/DocumentsUI/res/values-hu/strings.xml b/packages/DocumentsUI/res/values-hu/strings.xml
index aa9d799..627cac1 100644
--- a/packages/DocumentsUI/res/values-hu/strings.xml
+++ b/packages/DocumentsUI/res/values-hu/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokumentumok"</string>
+ <string name="app_label" msgid="1551050262492398204">"Fájlok"</string>
<string name="downloads_label" msgid="959113951084633612">"Letöltések"</string>
<string name="title_open" msgid="4353228937663917801">"Megnyitás innen"</string>
<string name="title_save" msgid="2433679664882857999">"Mentés ide"</string>
diff --git a/packages/DocumentsUI/res/values-hy-rAM/strings.xml b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
index fe4bcf8..15c05eb 100644
--- a/packages/DocumentsUI/res/values-hy-rAM/strings.xml
+++ b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Փաստաթղթեր"</string>
+ <string name="app_label" msgid="1551050262492398204">"Ֆայլեր"</string>
<string name="downloads_label" msgid="959113951084633612">"Ներբեռնումներ"</string>
<string name="title_open" msgid="4353228937663917801">"Բացել այստեղից"</string>
<string name="title_save" msgid="2433679664882857999">"Պահել այստեղ"</string>
diff --git a/packages/DocumentsUI/res/values-in/strings.xml b/packages/DocumentsUI/res/values-in/strings.xml
index 81f9f49..fb9fed6 100644
--- a/packages/DocumentsUI/res/values-in/strings.xml
+++ b/packages/DocumentsUI/res/values-in/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokumen"</string>
+ <string name="app_label" msgid="1551050262492398204">"File"</string>
<string name="downloads_label" msgid="959113951084633612">"Unduhan"</string>
<string name="title_open" msgid="4353228937663917801">"Buka dari"</string>
<string name="title_save" msgid="2433679664882857999">"Simpan ke"</string>
diff --git a/packages/DocumentsUI/res/values-is-rIS/strings.xml b/packages/DocumentsUI/res/values-is-rIS/strings.xml
index 6f86ad1..ff229ce 100644
--- a/packages/DocumentsUI/res/values-is-rIS/strings.xml
+++ b/packages/DocumentsUI/res/values-is-rIS/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Skjöl"</string>
+ <string name="app_label" msgid="1551050262492398204">"Skrár"</string>
<string name="downloads_label" msgid="959113951084633612">"Niðurhal"</string>
<string name="title_open" msgid="4353228937663917801">"Opna frá"</string>
<string name="title_save" msgid="2433679664882857999">"Vista í"</string>
diff --git a/packages/DocumentsUI/res/values-it/strings.xml b/packages/DocumentsUI/res/values-it/strings.xml
index 5ad9fb29..e7f1b68 100644
--- a/packages/DocumentsUI/res/values-it/strings.xml
+++ b/packages/DocumentsUI/res/values-it/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Documenti"</string>
+ <string name="app_label" msgid="1551050262492398204">"File"</string>
<string name="downloads_label" msgid="959113951084633612">"Download"</string>
<string name="title_open" msgid="4353228937663917801">"Apri da"</string>
<string name="title_save" msgid="2433679664882857999">"Salva in"</string>
diff --git a/packages/DocumentsUI/res/values-iw/strings.xml b/packages/DocumentsUI/res/values-iw/strings.xml
index 51414e3..ad24b4e 100644
--- a/packages/DocumentsUI/res/values-iw/strings.xml
+++ b/packages/DocumentsUI/res/values-iw/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"מסמכים"</string>
+ <string name="app_label" msgid="1551050262492398204">"קבצים"</string>
<string name="downloads_label" msgid="959113951084633612">"הורדות"</string>
<string name="title_open" msgid="4353228937663917801">"פתח מ-"</string>
<string name="title_save" msgid="2433679664882857999">"שמור ב-"</string>
diff --git a/packages/DocumentsUI/res/values-ja/strings.xml b/packages/DocumentsUI/res/values-ja/strings.xml
index 48f482a..e1b2c50 100644
--- a/packages/DocumentsUI/res/values-ja/strings.xml
+++ b/packages/DocumentsUI/res/values-ja/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"ドキュメント"</string>
+ <string name="app_label" msgid="1551050262492398204">"ファイル"</string>
<string name="downloads_label" msgid="959113951084633612">"ダウンロード"</string>
<string name="title_open" msgid="4353228937663917801">"次から開く:"</string>
<string name="title_save" msgid="2433679664882857999">"次に保存:"</string>
@@ -110,9 +110,9 @@
<string name="menu_rename" msgid="7678802479104285353">"名前を変更"</string>
<string name="rename_error" msgid="4203041674883412606">"ドキュメントの名前を変更できませんでした"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"一部のファイルが変換されました"</string>
- <string name="open_external_dialog_request" msgid="5789329484285817629">"「<xliff:g id="STORAGE"><i>^3</i></xliff:g>」の「<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>」ディレクトリに「<xliff:g id="APPNAME"><b>^1</b></xliff:g>」へのアクセスを許可しますか?"</string>
- <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"「<xliff:g id="APPNAME"><b>^1</b></xliff:g>」アプリに「<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>」ディレクトリへのアクセスを許可しますか?"</string>
- <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g>の写真や動画などのデータへのアクセスを「<xliff:g id="APPNAME"><b>^1</b></xliff:g>」に許可しますか?"</string>
+ <string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="STORAGE"><i>^3</i></xliff:g> の <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ディレクトリに <xliff:g id="APPNAME"><b>^1</b></xliff:g> へのアクセスを許可しますか?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> アプリに <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ディレクトリへのアクセスを許可しますか?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g>の写真や動画などのデータへのアクセスを <xliff:g id="APPNAME"><b>^1</b></xliff:g> に許可しますか?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"今後表示しない"</string>
<string name="allow" msgid="7225948811296386551">"許可"</string>
<string name="deny" msgid="2081879885755434506">"拒否"</string>
diff --git a/packages/DocumentsUI/res/values-ka-rGE/strings.xml b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
index 1be969c..024194f 100644
--- a/packages/DocumentsUI/res/values-ka-rGE/strings.xml
+++ b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"დოკუმენტები"</string>
+ <string name="app_label" msgid="1551050262492398204">"ფაილები"</string>
<string name="downloads_label" msgid="959113951084633612">"ჩამოტვირთვები"</string>
<string name="title_open" msgid="4353228937663917801">"გახსნა აქედან:"</string>
<string name="title_save" msgid="2433679664882857999">"შენახვა აქ:"</string>
diff --git a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
index dcb8464..27ac0a6 100644
--- a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
+++ b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Құжаттар"</string>
+ <string name="app_label" msgid="1551050262492398204">"Файлдар"</string>
<string name="downloads_label" msgid="959113951084633612">"Жүктеулер"</string>
<string name="title_open" msgid="4353228937663917801">"Мынадан ашу:"</string>
<string name="title_save" msgid="2433679664882857999">"Сақталатын орны"</string>
diff --git a/packages/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml
index 7c0e8c2..7e7843d 100644
--- a/packages/DocumentsUI/res/values-km-rKH/strings.xml
+++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"ឯកសារ"</string>
+ <string name="app_label" msgid="1551050262492398204">"ឯកសារ"</string>
<string name="downloads_label" msgid="959113951084633612">"ទាញយក"</string>
<string name="title_open" msgid="4353228937663917801">"បើកពី"</string>
<string name="title_save" msgid="2433679664882857999">"រក្សាទុកទៅ"</string>
diff --git a/packages/DocumentsUI/res/values-kn-rIN/strings.xml b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
index c7d414f..1eb499b 100644
--- a/packages/DocumentsUI/res/values-kn-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"ಡಾಕ್ಯುಮೆಂಟ್ಗಳು"</string>
+ <string name="app_label" msgid="1551050262492398204">"ಫೈಲ್ಗಳು"</string>
<string name="downloads_label" msgid="959113951084633612">"ಡೌನ್ಲೋಡ್ಗಳು"</string>
<string name="title_open" msgid="4353228937663917801">"ಇದರ ಮೂಲಕ ತೆರೆಯಿರಿ"</string>
<string name="title_save" msgid="2433679664882857999">"ಇವುಗಳಲ್ಲಿ ಉಳಿಸಿ"</string>
diff --git a/packages/DocumentsUI/res/values-ko/strings.xml b/packages/DocumentsUI/res/values-ko/strings.xml
index 8f5cc9f..5d2b162 100644
--- a/packages/DocumentsUI/res/values-ko/strings.xml
+++ b/packages/DocumentsUI/res/values-ko/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"문서"</string>
+ <string name="app_label" msgid="1551050262492398204">"파일"</string>
<string name="downloads_label" msgid="959113951084633612">"다운로드"</string>
<string name="title_open" msgid="4353228937663917801">"열기:"</string>
<string name="title_save" msgid="2433679664882857999">"저장 위치:"</string>
diff --git a/packages/DocumentsUI/res/values-ky-rKG/strings.xml b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
index db8b04c..be32765 100644
--- a/packages/DocumentsUI/res/values-ky-rKG/strings.xml
+++ b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Документтер"</string>
+ <string name="app_label" msgid="1551050262492398204">"Файлдар"</string>
<string name="downloads_label" msgid="959113951084633612">"Жүктөлүп алынгандар"</string>
<string name="title_open" msgid="4353228937663917801">"Кийинкиден ачуу:"</string>
<string name="title_save" msgid="2433679664882857999">"Кийинкиге сактоо:"</string>
diff --git a/packages/DocumentsUI/res/values-lo-rLA/strings.xml b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
index 6c2bfd6..fe4c0b6 100644
--- a/packages/DocumentsUI/res/values-lo-rLA/strings.xml
+++ b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"ເອກະສານ"</string>
+ <string name="app_label" msgid="1551050262492398204">"ໄຟລ໌"</string>
<string name="downloads_label" msgid="959113951084633612">"ການດາວໂຫລດ"</string>
<string name="title_open" msgid="4353228937663917801">"ເປີດຈາກ"</string>
<string name="title_save" msgid="2433679664882857999">"ບັນທຶກໄປທີ່"</string>
diff --git a/packages/DocumentsUI/res/values-lt/strings.xml b/packages/DocumentsUI/res/values-lt/strings.xml
index 6ee4fb8..0918616 100644
--- a/packages/DocumentsUI/res/values-lt/strings.xml
+++ b/packages/DocumentsUI/res/values-lt/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokumentai"</string>
+ <string name="app_label" msgid="1551050262492398204">"Failai"</string>
<string name="downloads_label" msgid="959113951084633612">"Atsisiuntimai"</string>
<string name="title_open" msgid="4353228937663917801">"Atidaryti iš"</string>
<string name="title_save" msgid="2433679664882857999">"Išsaugoti į"</string>
diff --git a/packages/DocumentsUI/res/values-lv/strings.xml b/packages/DocumentsUI/res/values-lv/strings.xml
index fa3a052..e767ea1 100644
--- a/packages/DocumentsUI/res/values-lv/strings.xml
+++ b/packages/DocumentsUI/res/values-lv/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokumenti"</string>
+ <string name="app_label" msgid="1551050262492398204">"Faili"</string>
<string name="downloads_label" msgid="959113951084633612">"Lejupielādes"</string>
<string name="title_open" msgid="4353228937663917801">"Atvēršana no:"</string>
<string name="title_save" msgid="2433679664882857999">"Saglabāšana:"</string>
diff --git a/packages/DocumentsUI/res/values-mk-rMK/strings.xml b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
index 14633df..b568211 100644
--- a/packages/DocumentsUI/res/values-mk-rMK/strings.xml
+++ b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Документи"</string>
+ <string name="app_label" msgid="1551050262492398204">"Датотеки"</string>
<string name="downloads_label" msgid="959113951084633612">"Преземања"</string>
<string name="title_open" msgid="4353228937663917801">"Отвори од"</string>
<string name="title_save" msgid="2433679664882857999">"Зачувај во"</string>
diff --git a/packages/DocumentsUI/res/values-ml-rIN/strings.xml b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
index 6f1bdfd..bdeeaa5 100644
--- a/packages/DocumentsUI/res/values-ml-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"പ്രമാണങ്ങൾ"</string>
+ <string name="app_label" msgid="1551050262492398204">"ഫയലുകൾ"</string>
<string name="downloads_label" msgid="959113951084633612">"ഡൗണ്ലോഡുകൾ"</string>
<string name="title_open" msgid="4353228937663917801">"ഇതിൽ നിന്നും തുറക്കുക"</string>
<string name="title_save" msgid="2433679664882857999">"ഇതില് സംരക്ഷിക്കുക"</string>
diff --git a/packages/DocumentsUI/res/values-mn-rMN/strings.xml b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
index da55ab0..b98125b 100644
--- a/packages/DocumentsUI/res/values-mn-rMN/strings.xml
+++ b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Документүүд"</string>
+ <string name="app_label" msgid="1551050262492398204">"Файл"</string>
<string name="downloads_label" msgid="959113951084633612">"Таталт"</string>
<string name="title_open" msgid="4353228937663917801">"Нээх"</string>
<string name="title_save" msgid="2433679664882857999">"Хадгалах"</string>
diff --git a/packages/DocumentsUI/res/values-mr-rIN/strings.xml b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
index 7453aa2..3ff4fc0 100644
--- a/packages/DocumentsUI/res/values-mr-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"दस्तऐवज"</string>
+ <string name="app_label" msgid="1551050262492398204">"फायली"</string>
<string name="downloads_label" msgid="959113951084633612">"डाउनलोड"</string>
<string name="title_open" msgid="4353228937663917801">"वरून उघडा"</string>
<string name="title_save" msgid="2433679664882857999">"येथे जतन करा"</string>
diff --git a/packages/DocumentsUI/res/values-ms-rMY/strings.xml b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
index 7180bc2..602a52d 100644
--- a/packages/DocumentsUI/res/values-ms-rMY/strings.xml
+++ b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokumen"</string>
+ <string name="app_label" msgid="1551050262492398204">"Fail"</string>
<string name="downloads_label" msgid="959113951084633612">"Muat turun"</string>
<string name="title_open" msgid="4353228937663917801">"Buka dari"</string>
<string name="title_save" msgid="2433679664882857999">"Simpan ke"</string>
diff --git a/packages/DocumentsUI/res/values-my-rMM/strings.xml b/packages/DocumentsUI/res/values-my-rMM/strings.xml
index fc7e05b..2b0cc1f 100644
--- a/packages/DocumentsUI/res/values-my-rMM/strings.xml
+++ b/packages/DocumentsUI/res/values-my-rMM/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"စာရွက်စာတန်းများ"</string>
+ <string name="app_label" msgid="1551050262492398204">"ဖိုင်များ"</string>
<string name="downloads_label" msgid="959113951084633612">"ဒေါင်းလုဒ်များ"</string>
<string name="title_open" msgid="4353228937663917801">"မှ ဖွင့်ပါ"</string>
<string name="title_save" msgid="2433679664882857999">"သို့ သိမ်းပါ"</string>
@@ -27,9 +27,9 @@
<string name="menu_search" msgid="3816712084502856974">"ရှာဖွေရန်"</string>
<string name="menu_settings" msgid="8239065133341597825">"သိုလှောင်မှု ဆက်တင်များ"</string>
<string name="menu_open" msgid="432922957274920903">"ဖွင့်ရန်"</string>
- <string name="menu_save" msgid="2394743337684426338">"သိမ်းပါ"</string>
+ <string name="menu_save" msgid="2394743337684426338">"သိမ်းရန်"</string>
<string name="menu_share" msgid="3075149983979628146">"မျှဝေခြင်း"</string>
- <string name="menu_delete" msgid="8138799623850614177">"ဖျက်ပစ်ရန်"</string>
+ <string name="menu_delete" msgid="8138799623850614177">"ဖျက်ရန်"</string>
<string name="menu_select_all" msgid="8323579667348729928">"အားလုံးကို ရွေးရန်"</string>
<string name="menu_copy" msgid="3612326052677229148">"…သို့ကူးယူရန်"</string>
<string name="menu_move" msgid="1828090633118079817">"...သို့ ရွှေ့ရန်"</string>
diff --git a/packages/DocumentsUI/res/values-nb/strings.xml b/packages/DocumentsUI/res/values-nb/strings.xml
index bd5da81..ab56341 100644
--- a/packages/DocumentsUI/res/values-nb/strings.xml
+++ b/packages/DocumentsUI/res/values-nb/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokumenter"</string>
+ <string name="app_label" msgid="1551050262492398204">"Filer"</string>
<string name="downloads_label" msgid="959113951084633612">"Nedlastinger"</string>
<string name="title_open" msgid="4353228937663917801">"Åpne fra"</string>
<string name="title_save" msgid="2433679664882857999">"Lagre i"</string>
diff --git a/packages/DocumentsUI/res/values-ne-rNP/strings.xml b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
index 0ab0b78..48fcbc1 100644
--- a/packages/DocumentsUI/res/values-ne-rNP/strings.xml
+++ b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"कागजातहरू"</string>
+ <string name="app_label" msgid="1551050262492398204">"फाइलहरू"</string>
<string name="downloads_label" msgid="959113951084633612">"डाउनलोडहरू"</string>
<string name="title_open" msgid="4353228937663917801">"यसबाट खोल्नुहोस्"</string>
<string name="title_save" msgid="2433679664882857999">"यसमा सुरक्षित गर्नुहोस्"</string>
diff --git a/packages/DocumentsUI/res/values-nl/strings.xml b/packages/DocumentsUI/res/values-nl/strings.xml
index 7ede6f5..07c41f8 100644
--- a/packages/DocumentsUI/res/values-nl/strings.xml
+++ b/packages/DocumentsUI/res/values-nl/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Documenten"</string>
+ <string name="app_label" msgid="1551050262492398204">"Bestanden"</string>
<string name="downloads_label" msgid="959113951084633612">"Downloads"</string>
<string name="title_open" msgid="4353228937663917801">"Openen vanuit"</string>
<string name="title_save" msgid="2433679664882857999">"Opslaan in"</string>
diff --git a/packages/DocumentsUI/res/values-pa-rIN/strings.xml b/packages/DocumentsUI/res/values-pa-rIN/strings.xml
index 7e04403..178e3b4 100644
--- a/packages/DocumentsUI/res/values-pa-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-pa-rIN/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"ਦਸਤਾਵੇਜ਼"</string>
+ <string name="app_label" msgid="1551050262492398204">"ਫ਼ਾਈਲਾਂ"</string>
<string name="downloads_label" msgid="959113951084633612">"ਡਾਊਨਲੋਡ"</string>
<string name="title_open" msgid="4353228937663917801">"ਤੋਂ ਖੋਲ੍ਹੋ"</string>
<string name="title_save" msgid="2433679664882857999">"ਇਸ ਵਿੱਚ ਰੱਖਿਅਤ ਕਰੋ"</string>
diff --git a/packages/DocumentsUI/res/values-pl/strings.xml b/packages/DocumentsUI/res/values-pl/strings.xml
index 2a66e59..6c17ed6 100644
--- a/packages/DocumentsUI/res/values-pl/strings.xml
+++ b/packages/DocumentsUI/res/values-pl/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokumenty"</string>
+ <string name="app_label" msgid="1551050262492398204">"Pliki"</string>
<string name="downloads_label" msgid="959113951084633612">"Pobrane"</string>
<string name="title_open" msgid="4353228937663917801">"Otwórz z"</string>
<string name="title_save" msgid="2433679664882857999">"Zapisz w"</string>
diff --git a/packages/DocumentsUI/res/values-pt-rBR/strings.xml b/packages/DocumentsUI/res/values-pt-rBR/strings.xml
index b86438f..21aa720 100644
--- a/packages/DocumentsUI/res/values-pt-rBR/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rBR/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Documentos"</string>
+ <string name="app_label" msgid="1551050262492398204">"Arquivos"</string>
<string name="downloads_label" msgid="959113951084633612">"Downloads"</string>
<string name="title_open" msgid="4353228937663917801">"Abrir de"</string>
<string name="title_save" msgid="2433679664882857999">"Salvar em"</string>
diff --git a/packages/DocumentsUI/res/values-pt-rPT/strings.xml b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
index e15662f..186defa 100644
--- a/packages/DocumentsUI/res/values-pt-rPT/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Documentos"</string>
+ <string name="app_label" msgid="1551050262492398204">"Ficheiros"</string>
<string name="downloads_label" msgid="959113951084633612">"Transferências"</string>
<string name="title_open" msgid="4353228937663917801">"Abrir de"</string>
<string name="title_save" msgid="2433679664882857999">"Guardar em"</string>
diff --git a/packages/DocumentsUI/res/values-pt/strings.xml b/packages/DocumentsUI/res/values-pt/strings.xml
index b86438f..21aa720 100644
--- a/packages/DocumentsUI/res/values-pt/strings.xml
+++ b/packages/DocumentsUI/res/values-pt/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Documentos"</string>
+ <string name="app_label" msgid="1551050262492398204">"Arquivos"</string>
<string name="downloads_label" msgid="959113951084633612">"Downloads"</string>
<string name="title_open" msgid="4353228937663917801">"Abrir de"</string>
<string name="title_save" msgid="2433679664882857999">"Salvar em"</string>
diff --git a/packages/DocumentsUI/res/values-ro/strings.xml b/packages/DocumentsUI/res/values-ro/strings.xml
index 1f519f5..e25fa1d 100644
--- a/packages/DocumentsUI/res/values-ro/strings.xml
+++ b/packages/DocumentsUI/res/values-ro/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Documente"</string>
+ <string name="app_label" msgid="1551050262492398204">"Fișiere"</string>
<string name="downloads_label" msgid="959113951084633612">"Descărcări"</string>
<string name="title_open" msgid="4353228937663917801">"Deschideți din"</string>
<string name="title_save" msgid="2433679664882857999">"Salvați în"</string>
diff --git a/packages/DocumentsUI/res/values-ru/strings.xml b/packages/DocumentsUI/res/values-ru/strings.xml
index 37cf065..37b1b45 100644
--- a/packages/DocumentsUI/res/values-ru/strings.xml
+++ b/packages/DocumentsUI/res/values-ru/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Документы"</string>
+ <string name="app_label" msgid="1551050262492398204">"Файлы"</string>
<string name="downloads_label" msgid="959113951084633612">"Загрузки"</string>
<string name="title_open" msgid="4353228937663917801">"Открыть"</string>
<string name="title_save" msgid="2433679664882857999">"Сохранить"</string>
diff --git a/packages/DocumentsUI/res/values-si-rLK/strings.xml b/packages/DocumentsUI/res/values-si-rLK/strings.xml
index bf94066..d43f5f3 100644
--- a/packages/DocumentsUI/res/values-si-rLK/strings.xml
+++ b/packages/DocumentsUI/res/values-si-rLK/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"ලේඛන"</string>
+ <string name="app_label" msgid="1551050262492398204">"ගොනු"</string>
<string name="downloads_label" msgid="959113951084633612">"බාගැනීම්"</string>
<string name="title_open" msgid="4353228937663917801">"විවෘත වන්නේ"</string>
<string name="title_save" msgid="2433679664882857999">"සුරකින්නේ"</string>
diff --git a/packages/DocumentsUI/res/values-sk/strings.xml b/packages/DocumentsUI/res/values-sk/strings.xml
index 9a4ee90..edc2027 100644
--- a/packages/DocumentsUI/res/values-sk/strings.xml
+++ b/packages/DocumentsUI/res/values-sk/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokumenty"</string>
+ <string name="app_label" msgid="1551050262492398204">"Súbory"</string>
<string name="downloads_label" msgid="959113951084633612">"Stiahnuté súbory"</string>
<string name="title_open" msgid="4353228937663917801">"Otvoriť z"</string>
<string name="title_save" msgid="2433679664882857999">"Uložiť do"</string>
diff --git a/packages/DocumentsUI/res/values-sl/strings.xml b/packages/DocumentsUI/res/values-sl/strings.xml
index 0c7816c..1779eef 100644
--- a/packages/DocumentsUI/res/values-sl/strings.xml
+++ b/packages/DocumentsUI/res/values-sl/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokumenti"</string>
+ <string name="app_label" msgid="1551050262492398204">"Datoteke"</string>
<string name="downloads_label" msgid="959113951084633612">"Prenosi"</string>
<string name="title_open" msgid="4353228937663917801">"Odpri iz mape"</string>
<string name="title_save" msgid="2433679664882857999">"Shrani v"</string>
diff --git a/packages/DocumentsUI/res/values-sq-rAL/strings.xml b/packages/DocumentsUI/res/values-sq-rAL/strings.xml
index 6cfe398..7e78481 100644
--- a/packages/DocumentsUI/res/values-sq-rAL/strings.xml
+++ b/packages/DocumentsUI/res/values-sq-rAL/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokumente"</string>
+ <string name="app_label" msgid="1551050262492398204">"Skedarët"</string>
<string name="downloads_label" msgid="959113951084633612">"Shkarkimet"</string>
<string name="title_open" msgid="4353228937663917801">"Hap nga"</string>
<string name="title_save" msgid="2433679664882857999">"Ruaje te"</string>
diff --git a/packages/DocumentsUI/res/values-sr/strings.xml b/packages/DocumentsUI/res/values-sr/strings.xml
index d44b89b..3935739 100644
--- a/packages/DocumentsUI/res/values-sr/strings.xml
+++ b/packages/DocumentsUI/res/values-sr/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Документи"</string>
+ <string name="app_label" msgid="1551050262492398204">"Датотеке"</string>
<string name="downloads_label" msgid="959113951084633612">"Преузимања"</string>
<string name="title_open" msgid="4353228937663917801">"Отвори са"</string>
<string name="title_save" msgid="2433679664882857999">"Сачувај у"</string>
diff --git a/packages/DocumentsUI/res/values-sv/strings.xml b/packages/DocumentsUI/res/values-sv/strings.xml
index 760a456..53a26b6 100644
--- a/packages/DocumentsUI/res/values-sv/strings.xml
+++ b/packages/DocumentsUI/res/values-sv/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokument"</string>
+ <string name="app_label" msgid="1551050262492398204">"Filer"</string>
<string name="downloads_label" msgid="959113951084633612">"Nedladdningar"</string>
<string name="title_open" msgid="4353228937663917801">"Öppna från"</string>
<string name="title_save" msgid="2433679664882857999">"Spara till"</string>
diff --git a/packages/DocumentsUI/res/values-sw/strings.xml b/packages/DocumentsUI/res/values-sw/strings.xml
index eb37ad0..9df96f4 100644
--- a/packages/DocumentsUI/res/values-sw/strings.xml
+++ b/packages/DocumentsUI/res/values-sw/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Hati"</string>
+ <string name="app_label" msgid="1551050262492398204">"Faili"</string>
<string name="downloads_label" msgid="959113951084633612">"Vipakuliwa"</string>
<string name="title_open" msgid="4353228937663917801">"Fungua kutoka"</string>
<string name="title_save" msgid="2433679664882857999">"Hifadhi kwenye"</string>
diff --git a/packages/DocumentsUI/res/values-ta-rIN/strings.xml b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
index 1f54641..1236647 100644
--- a/packages/DocumentsUI/res/values-ta-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"ஆவணங்கள்"</string>
+ <string name="app_label" msgid="1551050262492398204">"கோப்புகள்"</string>
<string name="downloads_label" msgid="959113951084633612">"இறக்கங்கள்"</string>
<string name="title_open" msgid="4353228937663917801">"இதில் திற"</string>
<string name="title_save" msgid="2433679664882857999">"இதில் சேமி"</string>
diff --git a/packages/DocumentsUI/res/values-te-rIN/strings.xml b/packages/DocumentsUI/res/values-te-rIN/strings.xml
index 7449913..e241841 100644
--- a/packages/DocumentsUI/res/values-te-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-te-rIN/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"పత్రాలు"</string>
+ <string name="app_label" msgid="1551050262492398204">"ఫైల్లు"</string>
<string name="downloads_label" msgid="959113951084633612">"డౌన్లోడ్లు"</string>
<string name="title_open" msgid="4353228937663917801">"ఇక్కడి నుండి తెరువు"</string>
<string name="title_save" msgid="2433679664882857999">"ఇందులో సేవ్ చేయి"</string>
diff --git a/packages/DocumentsUI/res/values-th/strings.xml b/packages/DocumentsUI/res/values-th/strings.xml
index 77cb9b1..ec3a3cd 100644
--- a/packages/DocumentsUI/res/values-th/strings.xml
+++ b/packages/DocumentsUI/res/values-th/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"เอกสาร"</string>
+ <string name="app_label" msgid="1551050262492398204">"ไฟล์"</string>
<string name="downloads_label" msgid="959113951084633612">"การดาวน์โหลด"</string>
<string name="title_open" msgid="4353228937663917801">"เปิดจาก"</string>
<string name="title_save" msgid="2433679664882857999">"บันทึกไปยัง"</string>
diff --git a/packages/DocumentsUI/res/values-tl/strings.xml b/packages/DocumentsUI/res/values-tl/strings.xml
index adf1b72..9271092 100644
--- a/packages/DocumentsUI/res/values-tl/strings.xml
+++ b/packages/DocumentsUI/res/values-tl/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Mga Dokumento"</string>
+ <string name="app_label" msgid="1551050262492398204">"Mga File"</string>
<string name="downloads_label" msgid="959113951084633612">"Mga Download"</string>
<string name="title_open" msgid="4353228937663917801">"Buksan mula sa"</string>
<string name="title_save" msgid="2433679664882857999">"I-save sa"</string>
diff --git a/packages/DocumentsUI/res/values-tr/strings.xml b/packages/DocumentsUI/res/values-tr/strings.xml
index bdc5983..24107c6 100644
--- a/packages/DocumentsUI/res/values-tr/strings.xml
+++ b/packages/DocumentsUI/res/values-tr/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Dokümanlar"</string>
+ <string name="app_label" msgid="1551050262492398204">"Dosyalar"</string>
<string name="downloads_label" msgid="959113951084633612">"İndirilenler"</string>
<string name="title_open" msgid="4353228937663917801">"Şuradan aç:"</string>
<string name="title_save" msgid="2433679664882857999">"Şuraya kaydet:"</string>
diff --git a/packages/DocumentsUI/res/values-uk/strings.xml b/packages/DocumentsUI/res/values-uk/strings.xml
index 192042c..1d6c4fd 100644
--- a/packages/DocumentsUI/res/values-uk/strings.xml
+++ b/packages/DocumentsUI/res/values-uk/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Документи"</string>
+ <string name="app_label" msgid="1551050262492398204">"Файли"</string>
<string name="downloads_label" msgid="959113951084633612">"Завантаження"</string>
<string name="title_open" msgid="4353228937663917801">"Відкрити"</string>
<string name="title_save" msgid="2433679664882857999">"Зберегти в"</string>
diff --git a/packages/DocumentsUI/res/values-ur-rPK/strings.xml b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
index bea7326..f098443 100644
--- a/packages/DocumentsUI/res/values-ur-rPK/strings.xml
+++ b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"دستاویزات"</string>
+ <string name="app_label" msgid="1551050262492398204">"فائلیں"</string>
<string name="downloads_label" msgid="959113951084633612">"ڈاؤن لوڈز"</string>
<string name="title_open" msgid="4353228937663917801">"کھولیں از"</string>
<string name="title_save" msgid="2433679664882857999">"اس میں محفوظ کریں"</string>
diff --git a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
index a3e117a..1d0e01b 100644
--- a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
+++ b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Hujjatlar"</string>
+ <string name="app_label" msgid="1551050262492398204">"Fayllar"</string>
<string name="downloads_label" msgid="959113951084633612">"Yuklanmalar"</string>
<string name="title_open" msgid="4353228937663917801">"Ochish"</string>
<string name="title_save" msgid="2433679664882857999">"Saqlash"</string>
diff --git a/packages/DocumentsUI/res/values-vi/strings.xml b/packages/DocumentsUI/res/values-vi/strings.xml
index 3979535..4e34725 100644
--- a/packages/DocumentsUI/res/values-vi/strings.xml
+++ b/packages/DocumentsUI/res/values-vi/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Tài liệu"</string>
+ <string name="app_label" msgid="1551050262492398204">"Tệp"</string>
<string name="downloads_label" msgid="959113951084633612">"Tải xuống"</string>
<string name="title_open" msgid="4353228937663917801">"Mở từ"</string>
<string name="title_save" msgid="2433679664882857999">"Lưu vào"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rCN/strings.xml b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
index d11362b..80b867c 100644
--- a/packages/DocumentsUI/res/values-zh-rCN/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"文档"</string>
+ <string name="app_label" msgid="1551050262492398204">"文件"</string>
<string name="downloads_label" msgid="959113951084633612">"下载"</string>
<string name="title_open" msgid="4353228937663917801">"打开文件"</string>
<string name="title_save" msgid="2433679664882857999">"保存文件"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rHK/strings.xml b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
index 971afdc..7984070 100644
--- a/packages/DocumentsUI/res/values-zh-rHK/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"文件"</string>
+ <string name="app_label" msgid="1551050262492398204">"檔案"</string>
<string name="downloads_label" msgid="959113951084633612">"下載"</string>
<string name="title_open" msgid="4353228937663917801">"開啟檔案"</string>
<string name="title_save" msgid="2433679664882857999">"儲存至"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rTW/strings.xml b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
index 16afeb4..0d591ae 100644
--- a/packages/DocumentsUI/res/values-zh-rTW/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"文件"</string>
+ <string name="app_label" msgid="1551050262492398204">"檔案"</string>
<string name="downloads_label" msgid="959113951084633612">"下載"</string>
<string name="title_open" msgid="4353228937663917801">"開啟檔案"</string>
<string name="title_save" msgid="2433679664882857999">"儲存至"</string>
diff --git a/packages/DocumentsUI/res/values-zu/strings.xml b/packages/DocumentsUI/res/values-zu/strings.xml
index 925f989..30b8ad1 100644
--- a/packages/DocumentsUI/res/values-zu/strings.xml
+++ b/packages/DocumentsUI/res/values-zu/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Amadokhumenti"</string>
+ <string name="app_label" msgid="1551050262492398204">"Amafayela"</string>
<string name="downloads_label" msgid="959113951084633612">"Okulandiwe"</string>
<string name="title_open" msgid="4353228937663917801">"Vula kusuka ku-"</string>
<string name="title_save" msgid="2433679664882857999">"Londoloza ku-"</string>
diff --git a/packages/DocumentsUI/res/values/colors.xml b/packages/DocumentsUI/res/values/colors.xml
index cf0643d..366a8a4 100644
--- a/packages/DocumentsUI/res/values/colors.xml
+++ b/packages/DocumentsUI/res/values/colors.xml
@@ -15,9 +15,6 @@
-->
<resources>
- <color name="material_grey_400">#ffbdbdbd</color>
- <color name="material_teal_700">#ff00796b</color>
-
<!-- This is the window background, but also the background for anything
else that needs to manually declare a background matching the "default"
app background (e.g. the drawer overlay). -->
@@ -26,22 +23,19 @@
<color name="directory_background">#fff7f7f7</color>
<color name="menu_search_background">@android:color/transparent</color>
- <color name="primary_dark">@*android:color/primary_dark_material_dark</color>
- <color name="primary">@*android:color/material_blue_grey_900</color>
- <color name="accent">@*android:color/accent_material_light</color>
- <color name="accent_dark">@*android:color/accent_material_dark</color>
- <color name="action_mode">@color/material_grey_400</color>
- <color name="status_bar_color">@*android:color/material_blue_grey_950</color>
+ <color name="primary_dark">@*android:color/primary_dark_device_default_settings</color>
+ <color name="primary">@*android:color/primary_device_default_settings</color>
+ <color name="accent">@*android:color/accent_device_default_light</color>
+ <color name="accent_dark">@*android:color/accent_device_default_dark</color>
<color name="band_select_background">#88ffffff</color>
<color name="band_select_border">#44000000</color>
- <color name="item_doc_background_disabled">#fff4f4f4</color>
-
- <color name="root_activated_color">@color/material_teal_700</color>
+ <color name="root_activated_color">@*android:color/accent_device_default_700</color>
<!-- TODO: Would be nice to move this to a color-set, but not sure how to support animation -->
<color name="item_doc_background">#fffafafa</color>
- <color name="item_doc_background_selected">#ffe0f2f1</color>
+ <color name="item_doc_background_disabled">#fff4f4f4</color>
+ <color name="item_doc_background_selected">@*android:color/accent_device_default_50</color>
</resources>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index e67cc8a..c2d4d04 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Title of the documents application [CHAR LIMIT=32] -->
- <string name="app_label">Documents</string>
+ <string name="app_label">Files</string>
<!-- Title of the standalone downloads activity. [CHAR LIMIT=32] -->
<string name="downloads_label">Downloads</string>
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index 9f09ebc..b5e32d4 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -28,9 +28,8 @@
<item name="android:colorPrimaryDark">@color/primary_dark</item>
<item name="android:colorPrimary">@color/primary</item>
<item name="android:colorAccent">@color/accent</item>
- <item name="colorActionMode">@color/action_mode</item>
+ <item name="android:colorControlActivated">?android:attr/colorAccent</item>
<item name="android:queryBackground">@color/menu_search_background</item>
- <item name="android:statusBarColor">@color/status_bar_color</item>
<item name="android:listDivider">@*android:drawable/list_divider_material</item>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
index c28fae8..177ba0d 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
@@ -23,181 +23,7 @@
import android.provider.DocumentsContract.Document;
import android.util.TypedValue;
-import java.util.HashMap;
-
public class IconUtils {
-
- private static HashMap<String, Integer> sMimeIcons = new HashMap<>();
-
- private static void add(String mimeType, int resId) {
- if (sMimeIcons.put(mimeType, resId) != null) {
- throw new RuntimeException(mimeType + " already registered!");
- }
- }
-
- static {
- int icon;
-
- // Package
- icon = R.drawable.ic_doc_apk;
- add("application/vnd.android.package-archive", icon);
-
- // Audio
- icon = R.drawable.ic_doc_audio;
- add("application/ogg", icon);
- add("application/x-flac", icon);
-
- // Certificate
- icon = R.drawable.ic_doc_certificate;
- add("application/pgp-keys", icon);
- add("application/pgp-signature", icon);
- add("application/x-pkcs12", icon);
- add("application/x-pkcs7-certreqresp", icon);
- add("application/x-pkcs7-crl", icon);
- add("application/x-x509-ca-cert", icon);
- add("application/x-x509-user-cert", icon);
- add("application/x-pkcs7-certificates", icon);
- add("application/x-pkcs7-mime", icon);
- add("application/x-pkcs7-signature", icon);
-
- // Source code
- icon = R.drawable.ic_doc_codes;
- add("application/rdf+xml", icon);
- add("application/rss+xml", icon);
- add("application/x-object", icon);
- add("application/xhtml+xml", icon);
- add("text/css", icon);
- add("text/html", icon);
- add("text/xml", icon);
- add("text/x-c++hdr", icon);
- add("text/x-c++src", icon);
- add("text/x-chdr", icon);
- add("text/x-csrc", icon);
- add("text/x-dsrc", icon);
- add("text/x-csh", icon);
- add("text/x-haskell", icon);
- add("text/x-java", icon);
- add("text/x-literate-haskell", icon);
- add("text/x-pascal", icon);
- add("text/x-tcl", icon);
- add("text/x-tex", icon);
- add("application/x-latex", icon);
- add("application/x-texinfo", icon);
- add("application/atom+xml", icon);
- add("application/ecmascript", icon);
- add("application/json", icon);
- add("application/javascript", icon);
- add("application/xml", icon);
- add("text/javascript", icon);
- add("application/x-javascript", icon);
-
- // Compressed
- icon = R.drawable.ic_doc_compressed;
- add("application/mac-binhex40", icon);
- add("application/rar", icon);
- add("application/zip", icon);
- add("application/x-apple-diskimage", icon);
- add("application/x-debian-package", icon);
- add("application/x-gtar", icon);
- add("application/x-iso9660-image", icon);
- add("application/x-lha", icon);
- add("application/x-lzh", icon);
- add("application/x-lzx", icon);
- add("application/x-stuffit", icon);
- add("application/x-tar", icon);
- add("application/x-webarchive", icon);
- add("application/x-webarchive-xml", icon);
- add("application/gzip", icon);
- add("application/x-7z-compressed", icon);
- add("application/x-deb", icon);
- add("application/x-rar-compressed", icon);
-
- // Contact
- icon = R.drawable.ic_doc_contact;
- add("text/x-vcard", icon);
- add("text/vcard", icon);
-
- // Event
- icon = R.drawable.ic_doc_event;
- add("text/calendar", icon);
- add("text/x-vcalendar", icon);
-
- // Font
- icon = R.drawable.ic_doc_font;
- add("application/x-font", icon);
- add("application/font-woff", icon);
- add("application/x-font-woff", icon);
- add("application/x-font-ttf", icon);
-
- // Image
- icon = R.drawable.ic_doc_image;
- add("application/vnd.oasis.opendocument.graphics", icon);
- add("application/vnd.oasis.opendocument.graphics-template", icon);
- add("application/vnd.oasis.opendocument.image", icon);
- add("application/vnd.stardivision.draw", icon);
- add("application/vnd.sun.xml.draw", icon);
- add("application/vnd.sun.xml.draw.template", icon);
-
- // PDF
- icon = R.drawable.ic_doc_pdf;
- add("application/pdf", icon);
-
- // Presentation
- icon = R.drawable.ic_doc_presentation;
- add("application/vnd.stardivision.impress", icon);
- add("application/vnd.sun.xml.impress", icon);
- add("application/vnd.sun.xml.impress.template", icon);
- add("application/x-kpresenter", icon);
- add("application/vnd.oasis.opendocument.presentation", icon);
-
- // Spreadsheet
- icon = R.drawable.ic_doc_spreadsheet;
- add("application/vnd.oasis.opendocument.spreadsheet", icon);
- add("application/vnd.oasis.opendocument.spreadsheet-template", icon);
- add("application/vnd.stardivision.calc", icon);
- add("application/vnd.sun.xml.calc", icon);
- add("application/vnd.sun.xml.calc.template", icon);
- add("application/x-kspread", icon);
-
- // Document
- icon = R.drawable.ic_doc_document;
- add("application/vnd.oasis.opendocument.text", icon);
- add("application/vnd.oasis.opendocument.text-master", icon);
- add("application/vnd.oasis.opendocument.text-template", icon);
- add("application/vnd.oasis.opendocument.text-web", icon);
- add("application/vnd.stardivision.writer", icon);
- add("application/vnd.stardivision.writer-global", icon);
- add("application/vnd.sun.xml.writer", icon);
- add("application/vnd.sun.xml.writer.global", icon);
- add("application/vnd.sun.xml.writer.template", icon);
- add("application/x-abiword", icon);
- add("application/x-kword", icon);
-
- // Video
- icon = R.drawable.ic_doc_video;
- add("application/x-quicktimeplayer", icon);
- add("application/x-shockwave-flash", icon);
-
- // Word
- icon = R.drawable.ic_doc_word;
- add("application/msword", icon);
- add("application/vnd.openxmlformats-officedocument.wordprocessingml.document", icon);
- add("application/vnd.openxmlformats-officedocument.wordprocessingml.template", icon);
-
- // Excel
- icon = R.drawable.ic_doc_excel;
- add("application/vnd.ms-excel", icon);
- add("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", icon);
- add("application/vnd.openxmlformats-officedocument.spreadsheetml.template", icon);
-
- // Powerpoint
- icon = R.drawable.ic_doc_powerpoint;
- add("application/vnd.ms-powerpoint", icon);
- add("application/vnd.openxmlformats-officedocument.presentationml.presentation", icon);
- add("application/vnd.openxmlformats-officedocument.presentationml.template", icon);
- add("application/vnd.openxmlformats-officedocument.presentationml.slideshow", icon);
- }
-
public static Drawable loadPackageIcon(Context context, String authority, int icon) {
if (icon != 0) {
if (authority != null) {
@@ -225,7 +51,7 @@
if (mode == State.MODE_GRID) {
return context.getDrawable(R.drawable.ic_grid_folder);
} else {
- return context.getDrawable(R.drawable.ic_doc_folder);
+ return context.getDrawable(com.android.internal.R.drawable.ic_doc_folder);
}
}
@@ -233,34 +59,7 @@
}
public static Drawable loadMimeIcon(Context context, String mimeType) {
- if (Document.MIME_TYPE_DIR.equals(mimeType)) {
- return context.getDrawable(R.drawable.ic_doc_folder);
- }
-
- // Look for exact match first
- Integer resId = sMimeIcons.get(mimeType);
- if (resId != null) {
- return context.getDrawable(resId);
- }
-
- if (mimeType == null) {
- // TODO: generic icon?
- return null;
- }
-
- // Otherwise look for partial match
- final String typeOnly = mimeType.split("/")[0];
- if ("audio".equals(typeOnly)) {
- return context.getDrawable(R.drawable.ic_doc_audio);
- } else if ("image".equals(typeOnly)) {
- return context.getDrawable(R.drawable.ic_doc_image);
- } else if ("text".equals(typeOnly)) {
- return context.getDrawable(R.drawable.ic_doc_text);
- } else if ("video".equals(typeOnly)) {
- return context.getDrawable(R.drawable.ic_doc_video);
- } else {
- return context.getDrawable(R.drawable.ic_doc_generic);
- }
+ return context.getContentResolver().getTypeDrawable(mimeType);
}
public static Drawable applyTintColor(Context context, int drawableId, int tintColorId) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
index af6aee7..5e3bbbb 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
@@ -28,6 +28,7 @@
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Build;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.support.annotation.Nullable;
@@ -73,7 +74,7 @@
@Nullable Intent build() {
if (DEBUG) Log.d(TAG, "Preparing intent for doc:" + mDocument.documentId);
- String trustedPkg = mResources.getString(R.string.trusted_quick_viewer_package);
+ String trustedPkg = getQuickViewPackage();
if (!TextUtils.isEmpty(trustedPkg)) {
Intent intent = new Intent(Intent.ACTION_QUICK_VIEW);
@@ -116,6 +117,16 @@
return null;
}
+ private String getQuickViewPackage() {
+ String resValue = mResources.getString(R.string.trusted_quick_viewer_package);
+ if (Build.IS_DEBUGGABLE ) {
+ // Allow users of debug devices to override default quick viewer
+ // for the purposes of testing.
+ return android.os.SystemProperties.get("debug.quick_viewer", resValue);
+ }
+ return resValue;
+ }
+
private int collectViewableUris(ArrayList<Uri> uris) {
final String[] siblingIds = mModel.getModelIds();
uris.ensureCapacity(siblingIds.length);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
index d830c61..a37590d 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.os.Bundle;
import android.text.Editable;
+import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -91,6 +92,14 @@
return false;
}
+ // Returning false in this method will bubble the event up to
+ // {@link BaseActivity#onKeyDown}. In order to prevent backspace popping
+ // documents once the textView is empty, we are going to trap it here.
+ if (keyCode == KeyEvent.KEYCODE_DEL
+ && TextUtils.isEmpty(mDisplayName.getText())) {
+ return true;
+ }
+
if (keyCode == KeyEvent.KEYCODE_ENTER && mSave.isEnabled()) {
performSave();
return true;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 870c343..b7c0a9c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -664,10 +664,10 @@
return true;
case R.id.menu_copy_to:
- transferDocuments(selection, FileOperationService.OPERATION_COPY);
// TODO: Only finish selection mode if copy-to is not canceled.
// Need to plum down into handling the way we do with deleteDocuments.
mode.finish();
+ transferDocuments(selection, FileOperationService.OPERATION_COPY);
return true;
case R.id.menu_move_to:
@@ -1391,7 +1391,7 @@
return mIconHelper.getDocumentIcon(mContext, doc.authority, doc.documentId,
doc.mimeType, doc.icon);
}
- return mContext.getDrawable(R.drawable.ic_doc_generic);
+ return mContext.getDrawable(com.android.internal.R.drawable.ic_doc_generic);
}
private String getTitle(List<DocumentInfo> docs) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index d54bdfd..649dde0 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -213,13 +213,13 @@
derivedIcon = R.drawable.ic_root_download;
} else if (isImages()) {
derivedType = TYPE_IMAGES;
- derivedIcon = R.drawable.ic_doc_image;
+ derivedIcon = com.android.internal.R.drawable.ic_doc_image;
} else if (isVideos()) {
derivedType = TYPE_VIDEO;
- derivedIcon = R.drawable.ic_doc_video;
+ derivedIcon = com.android.internal.R.drawable.ic_doc_video;
} else if (isAudio()) {
derivedType = TYPE_AUDIO;
- derivedIcon = R.drawable.ic_doc_audio;
+ derivedIcon = com.android.internal.R.drawable.ic_doc_audio;
} else if (isRecents()) {
derivedType = TYPE_RECENTS;
} else {
diff --git a/packages/EasterEgg/AndroidManifest.xml b/packages/EasterEgg/AndroidManifest.xml
index 50e8b5c..fbc2386 100644
--- a/packages/EasterEgg/AndroidManifest.xml
+++ b/packages/EasterEgg/AndroidManifest.xml
@@ -31,6 +31,7 @@
android:label="@string/app_name">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
+ <action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
diff --git a/packages/EasterEgg/res/drawable/collar.xml b/packages/EasterEgg/res/drawable/collar.xml
index 6c0d90a..5e4d0fd 100644
--- a/packages/EasterEgg/res/drawable/collar.xml
+++ b/packages/EasterEgg/res/drawable/collar.xml
@@ -18,5 +18,5 @@
android:height="48dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
- <path android:name="collar" android:fillColor="#FF000000" android:pathData="M9,18.4h30v1.6h-30z"/>
+ <path android:name="collar" android:fillColor="#FF000000" android:pathData="M9,18.4h30v1.7h-30z"/>
</vector>
diff --git a/packages/EasterEgg/res/drawable/leg1.xml b/packages/EasterEgg/res/drawable/leg1.xml
index 6257333..d72c746 100644
--- a/packages/EasterEgg/res/drawable/leg1.xml
+++ b/packages/EasterEgg/res/drawable/leg1.xml
@@ -18,5 +18,5 @@
android:height="48dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
- <path android:name="leg1" android:fillColor="#FF000000" android:pathData="M9,38h5v5h-5z"/>
+ <path android:name="leg1" android:fillColor="#FF000000" android:pathData="M9,37h5v6h-5z"/>
</vector>
diff --git a/packages/EasterEgg/res/drawable/leg2.xml b/packages/EasterEgg/res/drawable/leg2.xml
index 73352f6..a772a87 100644
--- a/packages/EasterEgg/res/drawable/leg2.xml
+++ b/packages/EasterEgg/res/drawable/leg2.xml
@@ -18,5 +18,5 @@
android:height="48dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
- <path android:name="leg2" android:fillColor="#FF000000" android:pathData="M16,38h5v5h-5z"/>
+ <path android:name="leg2" android:fillColor="#FF000000" android:pathData="M16,37h5v6h-5z"/>
</vector>
diff --git a/packages/EasterEgg/res/drawable/leg2_shadow.xml b/packages/EasterEgg/res/drawable/leg2_shadow.xml
index 77f4893..b01bd69 100644
--- a/packages/EasterEgg/res/drawable/leg2_shadow.xml
+++ b/packages/EasterEgg/res/drawable/leg2_shadow.xml
@@ -18,5 +18,5 @@
android:height="48dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
- <path android:name="leg2_shadow" android:fillColor="#FF000000" android:pathData="M16,38h5v1h-5z"/>
+ <path android:name="leg2_shadow" android:fillColor="#FF000000" android:pathData="M16,37h5v3h-5z"/>
</vector>
diff --git a/packages/EasterEgg/res/drawable/leg3.xml b/packages/EasterEgg/res/drawable/leg3.xml
index 53dea5c..d471236 100644
--- a/packages/EasterEgg/res/drawable/leg3.xml
+++ b/packages/EasterEgg/res/drawable/leg3.xml
@@ -18,5 +18,5 @@
android:height="48dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
- <path android:name="leg3" android:fillColor="#FF000000" android:pathData="M27,38h5v5h-5z"/>
+ <path android:name="leg3" android:fillColor="#FF000000" android:pathData="M27,37h5v6h-5z"/>
</vector>
diff --git a/packages/EasterEgg/res/drawable/leg4.xml b/packages/EasterEgg/res/drawable/leg4.xml
index f2ce73e..e5868eb 100644
--- a/packages/EasterEgg/res/drawable/leg4.xml
+++ b/packages/EasterEgg/res/drawable/leg4.xml
@@ -18,5 +18,5 @@
android:height="48dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
- <path android:name="leg4" android:fillColor="#FF000000" android:pathData="M34,38h5v5h-5z"/>
+ <path android:name="leg4" android:fillColor="#FF000000" android:pathData="M34,37h5v6h-5z"/>
</vector>
diff --git a/packages/EasterEgg/res/layout/cat_view.xml b/packages/EasterEgg/res/layout/cat_view.xml
index 82ced2f2..85b494d 100644
--- a/packages/EasterEgg/res/layout/cat_view.xml
+++ b/packages/EasterEgg/res/layout/cat_view.xml
@@ -20,12 +20,14 @@
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:gravity="center_horizontal"
android:clipToPadding="false">
<FrameLayout
- android:layout_width="wrap_content"
+ android:layout_width="96dp"
android:layout_height="wrap_content">
<ImageView
@@ -33,6 +35,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
+ android:layout_gravity="center"
android:scaleType="fitCenter" />
<LinearLayout
diff --git a/packages/EasterEgg/res/values/dimens.xml b/packages/EasterEgg/res/values/dimens.xml
new file mode 100644
index 0000000..e9dcebd
--- /dev/null
+++ b/packages/EasterEgg/res/values/dimens.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <dimen name="neko_display_size">64dp</dimen>
+</resources>
diff --git a/packages/EasterEgg/res/values/strings.xml b/packages/EasterEgg/res/values/strings.xml
index a2440c7b..8478a43 100644
--- a/packages/EasterEgg/res/values/strings.xml
+++ b/packages/EasterEgg/res/values/strings.xml
@@ -21,6 +21,7 @@
<string name="notification_title" translatable="false">A cat is here.</string>
<string name="default_cat_name" translatable="false">Cat #%s</string>
<string name="directory_name" translatable="false">Cats</string>
+ <string name="confirm_delete" translatable="false">Forget %s?</string>
<string-array name="food_names" translatable="false">
<item>Empty dish</item>
<item>Bits</item>
diff --git a/packages/EasterEgg/src/com/android/egg/neko/Cat.java b/packages/EasterEgg/src/com/android/egg/neko/Cat.java
index 864b20c..a4df372 100644
--- a/packages/EasterEgg/src/com/android/egg/neko/Cat.java
+++ b/packages/EasterEgg/src/com/android/egg/neko/Cat.java
@@ -24,10 +24,12 @@
import android.graphics.drawable.Icon;
import android.os.Bundle;
+import java.io.ByteArrayOutputStream;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import com.android.egg.R;
+import com.android.internal.logging.MetricsLogger;
public class Cat extends Drawable {
public static final long[] PURR = {0, 40, 20, 40, 20, 40, 20, 40, 20, 40, 20, 40};
@@ -37,6 +39,8 @@
private long mSeed;
private String mName;
private int mBodyColor;
+ private int mFootType;
+ private boolean mBowTie;
private synchronized Random notSoRandom(long seed) {
if (mNotSoRandom == null) {
@@ -66,6 +70,15 @@
return a[i+1];
}
+ public static final int getColorIndex(int q, int[] a) {
+ for(int i = 1; i < a.length; i+=2) {
+ if (a[i] == q) {
+ return i/2;
+ }
+ }
+ return -1;
+ }
+
public static final int[] P_BODY_COLORS = {
180, 0xFF212121, // black
180, 0xFFFFFFFF, // white
@@ -130,7 +143,7 @@
mSeed = seed;
setName(context.getString(R.string.default_cat_name,
- String.valueOf(mSeed).substring(0, 3)));
+ String.valueOf(mSeed % 1000)));
final Random nsr = notSoRandom(seed);
@@ -155,14 +168,19 @@
tint(0xFF000000, D.mouth, D.nose);
}
+ mFootType = 0;
if (nsr.nextFloat() < 0.25f) {
+ mFootType = 4;
tint(0xFFFFFFFF, D.foot1, D.foot2, D.foot3, D.foot4);
} else {
if (nsr.nextFloat() < 0.25f) {
- tint(0xFFFFFFFF, D.foot1, D.foot2);
+ mFootType = 2;
+ tint(0xFFFFFFFF, D.foot1, D.foot3);
} else if (nsr.nextFloat() < 0.25f) {
- tint(0xFFFFFFFF, D.foot3, D.foot4);
+ mFootType = 3; // maybe -2 would be better? meh.
+ tint(0xFFFFFFFF, D.foot2, D.foot4);
} else if (nsr.nextFloat() < 0.1f) {
+ mFootType = 1;
tint(0xFFFFFFFF, (Drawable) choose(nsr, D.foot1, D.foot2, D.foot3, D.foot4));
}
}
@@ -175,7 +193,8 @@
final int collarColor = chooseP(nsr, P_COLLAR_COLORS);
tint(collarColor, D.collar);
- tint((nsr.nextFloat() < 0.1f) ? collarColor : 0, D.bowtie);
+ mBowTie = nsr.nextFloat() < 0.1f;
+ tint(mBowTie ? collarColor : 0, D.bowtie);
}
public static Cat create(Context context) {
@@ -190,7 +209,7 @@
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return new Notification.Builder(context)
.setSmallIcon(Icon.createWithResource(context, R.drawable.stat_icon))
- .setLargeIcon(createLargeIcon(context))
+ .setLargeIcon(createNotificationLargeIcon(context))
.setColor(getBodyColor())
.setPriority(Notification.PRIORITY_LOW)
.setContentTitle(context.getString(R.string.notification_title))
@@ -240,11 +259,24 @@
return result;
}
- public Icon createLargeIcon(Context context) {
- final Resources res = context.getResources();
- final int w = res.getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
- final int h = res.getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
+ public static Icon recompressIcon(Icon bitmapIcon) {
+ if (bitmapIcon.getType() != Icon.TYPE_BITMAP) return bitmapIcon;
+ final Bitmap bits = bitmapIcon.getBitmap();
+ final ByteArrayOutputStream ostream = new ByteArrayOutputStream(
+ bits.getWidth() * bits.getHeight() * 2); // guess 50% compression
+ final boolean ok = bits.compress(Bitmap.CompressFormat.PNG, 100, ostream);
+ if (!ok) return null;
+ return Icon.createWithData(ostream.toByteArray(), 0, ostream.size());
+ }
+ public Icon createNotificationLargeIcon(Context context) {
+ final Resources res = context.getResources();
+ final int w = 2*res.getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
+ final int h = 2*res.getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
+ return recompressIcon(createIcon(context, w, h));
+ }
+
+ public Icon createIcon(Context context, int w, int h) {
Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(result);
final Paint pt = new Paint();
@@ -290,6 +322,30 @@
return mBodyColor;
}
+ public void logAdd(Context context) {
+ logCatAction(context, "egg_neko_add");
+ }
+
+ public void logRename(Context context) {
+ logCatAction(context, "egg_neko_rename");
+ }
+
+ public void logRemove(Context context) {
+ logCatAction(context, "egg_neko_remove");
+ }
+
+ public void logShare(Context context) {
+ logCatAction(context, "egg_neko_share");
+ }
+
+ private void logCatAction(Context context, String prefix) {
+ MetricsLogger.count(context, prefix, 1);
+ MetricsLogger.histogram(context, prefix +"_color",
+ getColorIndex(mBodyColor, P_BODY_COLORS));
+ MetricsLogger.histogram(context, prefix + "_bowtie", mBowTie ? 1 : 0);
+ MetricsLogger.histogram(context, prefix + "_feet", mFootType);
+ }
+
public static class CatParts {
public Drawable leftEar;
public Drawable rightEar;
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java b/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java
index 88a7968..c0b725c 100644
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java
@@ -20,6 +20,8 @@
import android.util.Log;
import android.widget.Toast;
+import com.android.internal.logging.MetricsLogger;
+
public class NekoActivationActivity extends Activity {
private void toastUp(String s) {
Toast toast = Toast.makeText(this, s, Toast.LENGTH_SHORT);
@@ -39,6 +41,7 @@
}
pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
+ MetricsLogger.histogram(this, "egg_neko_enable", 0);
toastUp("\uD83D\uDEAB");
} else {
if (NekoLand.DEBUG) {
@@ -46,6 +49,7 @@
}
pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
+ MetricsLogger.histogram(this, "egg_neko_enable", 1);
toastUp("\uD83D\uDC31");
}
finish();
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java b/packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java
index a2ffd3e..2d2fbe8 100644
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java
@@ -26,6 +26,7 @@
import android.widget.TextView;
import com.android.egg.R;
+import com.android.internal.logging.MetricsLogger;
import java.util.ArrayList;
@@ -51,6 +52,7 @@
if (currentState == 0 && food.getType() != 0) {
NekoService.registerJob(getContext(), food.getInterval(getContext()));
}
+ MetricsLogger.histogram(getContext(), "egg_neko_offered_food", food.getType());
prefs.setFoodState(food.getType());
dismiss();
}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java b/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java
index e6a4177..f59f0d9 100644
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java
@@ -24,6 +24,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.media.MediaScannerConnection;
import android.net.Uri;
@@ -44,16 +45,22 @@
import com.android.egg.R;
import com.android.egg.neko.PrefState.PrefsListener;
+import com.android.internal.logging.MetricsLogger;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
public class NekoLand extends Activity implements PrefsListener {
public static boolean DEBUG = false;
public static boolean DEBUG_NOTIFICATIONS = false;
+ private static final int EXPORT_BITMAP_SIZE = 600;
+
private static final int STORAGE_PERM_REQUEST = 123;
private static boolean CAT_GEN = false;
@@ -79,7 +86,8 @@
mAdapter = new CatAdapter();
recyclerView.setAdapter(mAdapter);
recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
- updateCats();
+ int numCats = updateCats();
+ MetricsLogger.histogram(this, "egg_neko_visit_gallery", numCats);
}
@Override
@@ -88,7 +96,7 @@
mPrefs.setListener(null);
}
- private void updateCats() {
+ private int updateCats() {
Cat[] cats;
if (CAT_GEN) {
cats = new Cat[50];
@@ -96,9 +104,22 @@
cats[i] = Cat.create(this);
}
} else {
- cats = mPrefs.getCats().toArray(new Cat[0]);
+ final float[] hsv = new float[3];
+ List<Cat> list = mPrefs.getCats();
+ Collections.sort(list, new Comparator<Cat>() {
+ @Override
+ public int compare(Cat cat, Cat cat2) {
+ Color.colorToHSV(cat.getBodyColor(), hsv);
+ float bodyH1 = hsv[0];
+ Color.colorToHSV(cat2.getBodyColor(), hsv);
+ float bodyH2 = hsv[0];
+ return Float.compare(bodyH1, bodyH2);
+ }
+ });
+ cats = list.toArray(new Cat[0]);
}
mAdapter.setCats(cats);
+ return cats.length;
}
private void onCatClick(Cat cat) {
@@ -115,18 +136,21 @@
}
private void onCatRemove(Cat cat) {
+ cat.logRemove(this);
mPrefs.removeCat(cat);
}
private void showNameDialog(final Cat cat) {
- Context context = new ContextThemeWrapper(this,
+ final Context context = new ContextThemeWrapper(this,
android.R.style.Theme_Material_Light_Dialog_NoActionBar);
// TODO: Move to XML, add correct margins.
View view = LayoutInflater.from(context).inflate(R.layout.edit_text, null);
final EditText text = (EditText) view.findViewById(android.R.id.edit);
text.setText(cat.getName());
text.setSelection(cat.getName().length());
- Drawable catIcon = cat.createLargeIcon(this).loadDrawable(this);
+ final int size = context.getResources()
+ .getDimensionPixelSize(android.R.dimen.app_icon_size);
+ Drawable catIcon = cat.createIcon(this, size, size).loadDrawable(this);
new AlertDialog.Builder(context)
.setTitle(" ")
.setIcon(catIcon)
@@ -134,6 +158,7 @@
.setPositiveButton(android.R.string.ok, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
+ cat.logRename(context);
cat.setName(text.getText().toString().trim());
mPrefs.addCat(cat);
}
@@ -160,10 +185,36 @@
.inflate(R.layout.cat_view, parent, false));
}
+ private void setContextGroupVisible(final CatHolder holder, boolean vis) {
+ final View group = holder.contextGroup;
+ if (vis && group.getVisibility() != View.VISIBLE) {
+ group.setAlpha(0);
+ group.setVisibility(View.VISIBLE);
+ group.animate().alpha(1.0f).setDuration(333);
+ Runnable hideAction = new Runnable() {
+ @Override
+ public void run() {
+ setContextGroupVisible(holder, false);
+ }
+ };
+ group.setTag(hideAction);
+ group.postDelayed(hideAction, 5000);
+ } else if (!vis && group.getVisibility() == View.VISIBLE) {
+ group.removeCallbacks((Runnable) group.getTag());
+ group.animate().alpha(0f).setDuration(250).withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ group.setVisibility(View.INVISIBLE);
+ }
+ });
+ }
+ }
+
@Override
public void onBindViewHolder(final CatHolder holder, int position) {
Context context = holder.itemView.getContext();
- holder.imageView.setImageIcon(mCats[position].createLargeIcon(context));
+ final int size = context.getResources().getDimensionPixelSize(R.dimen.neko_display_size);
+ holder.imageView.setImageIcon(mCats[position].createIcon(context, size, size));
holder.textView.setText(mCats[position].getName());
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
@@ -174,30 +225,30 @@
holder.itemView.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
- holder.contextGroup.removeCallbacks((Runnable) holder.contextGroup.getTag());
- holder.contextGroup.setVisibility(View.VISIBLE);
- Runnable hideAction = new Runnable() {
- @Override
- public void run() {
- holder.contextGroup.setVisibility(View.INVISIBLE);
- }
- };
- holder.contextGroup.setTag(hideAction);
- holder.contextGroup.postDelayed(hideAction, 5000);
+ setContextGroupVisible(holder, true);
return true;
}
});
holder.delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- holder.contextGroup.setVisibility(View.INVISIBLE);
- holder.contextGroup.removeCallbacks((Runnable) holder.contextGroup.getTag());
- onCatRemove(mCats[holder.getAdapterPosition()]);
+ setContextGroupVisible(holder, false);
+ new AlertDialog.Builder(NekoLand.this)
+ .setTitle(getString(R.string.confirm_delete, mCats[position].getName()))
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ onCatRemove(mCats[holder.getAdapterPosition()]);
+ }
+ })
+ .show();
}
});
holder.share.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
+ setContextGroupVisible(holder, false);
Cat cat = mCats[holder.getAdapterPosition()];
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
@@ -227,7 +278,7 @@
return;
}
final File png = new File(dir, cat.getName().replaceAll("[/ #:]+", "_") + ".png");
- Bitmap bitmap = cat.createBitmap(512, 512);
+ Bitmap bitmap = cat.createBitmap(EXPORT_BITMAP_SIZE, EXPORT_BITMAP_SIZE);
if (bitmap != null) {
try {
OutputStream os = new FileOutputStream(png);
@@ -244,6 +295,7 @@
intent.putExtra(Intent.EXTRA_SUBJECT, cat.getName());
intent.setType("image/png");
startActivity(Intent.createChooser(intent, null));
+ cat.logShare(this);
} catch (IOException e) {
Log.e("NekoLand", "save: error: " + e);
}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoService.java b/packages/EasterEgg/src/com/android/egg/neko/NekoService.java
index 1ee3851..32e3358 100644
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoService.java
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoService.java
@@ -82,6 +82,7 @@
if (cats.size() == 0 || rng.nextFloat() <= new_cat_prob) {
cat = Cat.create(this);
prefs.addCat(cat);
+ cat.logAdd(this);
Log.v(TAG, "A new cat is here: " + cat.getName());
} else {
cat = cats.get(rng.nextInt(cats.size()));
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java b/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java
index d5e143c..8a3ec8f 100644
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java
@@ -20,6 +20,7 @@
import android.util.Log;
import com.android.egg.neko.PrefState.PrefsListener;
+import com.android.internal.logging.MetricsLogger;
public class NekoTile extends TileService implements PrefsListener {
@@ -47,6 +48,18 @@
}
@Override
+ public void onTileAdded() {
+ super.onTileAdded();
+ MetricsLogger.count(this, "egg_neko_tile_added", 1);
+ }
+
+ @Override
+ public void onTileRemoved() {
+ super.onTileRemoved();
+ MetricsLogger.count(this, "egg_neko_tile_removed", 1);
+ }
+
+ @Override
public void onPrefsChanged() {
updateState();
}
@@ -65,6 +78,7 @@
public void onClick() {
if (mPrefs.getFoodState() != 0) {
// there's already food loaded, let's empty it
+ MetricsLogger.count(this, "egg_neko_empty_food", 1);
mPrefs.setFoodState(0);
NekoService.cancelJob(this);
} else {
@@ -91,6 +105,7 @@
private void showNekoDialog() {
Log.d(TAG, "showNekoDialog");
+ MetricsLogger.count(this, "egg_neko_select_food", 1);
showDialog(new NekoDialog(this));
}
}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 62f33bf..78b9927 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -484,6 +484,8 @@
@Override
public void deleteDocument(String docId) throws FileNotFoundException {
final File file = getFileForDocId(docId);
+ final File visibleFile = getFileForDocId(docId, true);
+
final boolean isDirectory = file.isDirectory();
if (isDirectory) {
FileUtils.deleteContents(file);
@@ -492,23 +494,25 @@
throw new IllegalStateException("Failed to delete " + file);
}
- final ContentResolver resolver = getContext().getContentResolver();
- final Uri externalUri = MediaStore.Files.getContentUri("external");
+ if (visibleFile != null) {
+ final ContentResolver resolver = getContext().getContentResolver();
+ final Uri externalUri = MediaStore.Files.getContentUri("external");
- // Remove media store entries for any files inside this directory, using
- // path prefix match. Logic borrowed from MtpDatabase.
- if (isDirectory) {
- final String path = file.getAbsolutePath() + "/";
+ // Remove media store entries for any files inside this directory, using
+ // path prefix match. Logic borrowed from MtpDatabase.
+ if (isDirectory) {
+ final String path = visibleFile.getAbsolutePath() + "/";
+ resolver.delete(externalUri,
+ "_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)",
+ new String[] { path + "%", Integer.toString(path.length()), path });
+ }
+
+ // Remove media store entry for this exact file.
+ final String path = visibleFile.getAbsolutePath();
resolver.delete(externalUri,
- "_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)",
- new String[] { path + "%", Integer.toString(path.length()), path });
+ "_data LIKE ?1 AND lower(_data)=lower(?2)",
+ new String[] { path, path });
}
-
- // Remove media store entry for this exact file.
- final String path = file.getAbsolutePath();
- resolver.delete(externalUri,
- "_data LIKE ?1 AND lower(_data)=lower(?2)",
- new String[] { path, path });
}
@Override
diff --git a/packages/Keyguard/res/layout/keyguard_password_view.xml b/packages/Keyguard/res/layout/keyguard_password_view.xml
index 7d45017..29c93d5 100644
--- a/packages/Keyguard/res/layout/keyguard_password_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_password_view.xml
@@ -26,7 +26,6 @@
androidprv:layout_maxWidth="@dimen/keyguard_security_width"
androidprv:layout_maxHeight="@dimen/keyguard_security_height"
android:gravity="bottom"
- android:contentDescription="@string/keyguard_accessibility_password_unlock"
>
<Space
diff --git a/packages/Keyguard/res/layout/keyguard_pin_view.xml b/packages/Keyguard/res/layout/keyguard_pin_view.xml
index d3fb982..e75f3c15 100644
--- a/packages/Keyguard/res/layout/keyguard_pin_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_pin_view.xml
@@ -26,7 +26,6 @@
androidprv:layout_maxWidth="@dimen/keyguard_security_width"
androidprv:layout_maxHeight="@dimen/keyguard_security_max_height"
android:orientation="vertical"
- android:contentDescription="@string/keyguard_accessibility_pin_unlock"
>
<include layout="@layout/keyguard_message_area"
android:layout_width="match_parent"
diff --git a/packages/Keyguard/res/values-af/strings.xml b/packages/Keyguard/res/values-af/strings.xml
index 568d82a..a338165 100644
--- a/packages/Keyguard/res/values-af/strings.xml
+++ b/packages/Keyguard/res/values-af/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-kaart is gesluit."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-kaart is PUK-geslote."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Ontsluit tans SIM-kaart…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Patroon ontsluit."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"PIN ontsluit."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Wagwoord ontsluit."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Patroonarea."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Sleep-area."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN-area"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM-PIN-area"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM-PUK-area"</string>
diff --git a/packages/Keyguard/res/values-am/strings.xml b/packages/Keyguard/res/values-am/strings.xml
index 68e9ff3..67fdc32 100644
--- a/packages/Keyguard/res/values-am/strings.xml
+++ b/packages/Keyguard/res/values-am/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"ሲም ካርድ ተዘግቷል።"</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"ሲም ካርድ በፒዩኬ ተዘግቷል።"</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"ሲም ካርዱን በመክፈት ላይ…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"በስርዓተ-ጥለት መክፈት።"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"በፒን መክፈት።"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"በይለፍ ቃል መክፈት።"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"የስርዓተ-ጥለት አካባቢ።"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"የማንሸራተቻ አካባቢ።"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"የፒን አካባቢ"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"የሲም ፒን አካባቢ"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"የሲም PUK አካባቢ"</string>
diff --git a/packages/Keyguard/res/values-ar/strings.xml b/packages/Keyguard/res/values-ar/strings.xml
index 09b78e4..8438699 100644
--- a/packages/Keyguard/res/values-ar/strings.xml
+++ b/packages/Keyguard/res/values-ar/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"شريحة SIM مؤمّنة."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"شريحة SIM مؤمّنة بكود PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"جارٍ إلغاء تأمين شريحة SIM…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"إلغاء القفل باستخدام النقش."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"إلغاء القفل باستخدام رمز PIN."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"إلغاء القفل باستخدام كلمة المرور."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"منطقة النقش."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"منطقة التمرير."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"منطقة رقم التعريف الشخصي"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"منطقة رقم التعريف الشخصي لبطاقة SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"منطقة PUK لبطاقة SIM"</string>
diff --git a/packages/Keyguard/res/values-az-rAZ/strings.xml b/packages/Keyguard/res/values-az-rAZ/strings.xml
index a8a1155..c7a8091 100644
--- a/packages/Keyguard/res/values-az-rAZ/strings.xml
+++ b/packages/Keyguard/res/values-az-rAZ/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM kart kilidlənib."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SİM kart PUK ilə kilidlənib."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SİM kartın kilidi açılır..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Kild açma modeli."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Pin kilid açması."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Şifrə kilidi."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Model sahəsi."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Sürüşdürmə sahəsi."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN sahəsi"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN sahəsi"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK sahəsi"</string>
diff --git a/packages/Keyguard/res/values-b+sr+Latn/strings.xml b/packages/Keyguard/res/values-b+sr+Latn/strings.xml
index 22dc059..570f4bc 100644
--- a/packages/Keyguard/res/values-b+sr+Latn/strings.xml
+++ b/packages/Keyguard/res/values-b+sr+Latn/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM kartica je zaključana."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM kartica je zaključana PUK kodom."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Otključavanje SIM kartice…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Otključavanje šablonom."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Otključavanje PIN-om."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Otključavanje lozinkom."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Oblast šablona."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Oblast prevlačenja."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Oblast za PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Oblast za PIN za SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Oblast za PUK za SIM"</string>
diff --git a/packages/Keyguard/res/values-be-rBY/strings.xml b/packages/Keyguard/res/values-be-rBY/strings.xml
index ca6a476..f357961 100644
--- a/packages/Keyguard/res/values-be-rBY/strings.xml
+++ b/packages/Keyguard/res/values-be-rBY/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-карта заблакiраваная."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-карта заблакiравана PUK-кодам."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Разблакiраванне SIM-карты..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Узор разблакiроўкі."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"PIN-код разблакiроўкі."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Пароль разблакiроўкі."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Вобласць узора."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Вобласць слайда."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Поле для PIN-кода"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Поле для PIN-кода SIM-карты"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Поле для PUK-кода SIM-карты"</string>
diff --git a/packages/Keyguard/res/values-bg/strings.xml b/packages/Keyguard/res/values-bg/strings.xml
index 7eb2dbd..988e97f 100644
--- a/packages/Keyguard/res/values-bg/strings.xml
+++ b/packages/Keyguard/res/values-bg/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM картата е заключена."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM картата е заключена с PUK код."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM картата се отключва…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Отключване с фигура."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Отключване с ПИН код."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Отключване с парола."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Област на фигурата."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Област на плъзгане."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Област за ПИН кода"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Област за ПИН кода на SIM картата"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Област за PUK кода на SIM картата"</string>
diff --git a/packages/Keyguard/res/values-bn-rBD/strings.xml b/packages/Keyguard/res/values-bn-rBD/strings.xml
index 7a33e21..1a2a8dd 100644
--- a/packages/Keyguard/res/values-bn-rBD/strings.xml
+++ b/packages/Keyguard/res/values-bn-rBD/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"সিম কার্ড লক করা আছে৷"</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"সিম কার্ডটি PUK কোড দিয়ে লক করা আছে৷"</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"সিম কার্ড আনলক করা হচ্ছে…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"প্যাটার্ন দিয়ে আনলক৷"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"পিন দিয়ে আনলক৷"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"পাসওয়ার্ড দিয়ে আনলক৷"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"প্যাটার্ন এলাকা৷"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"স্লাইড করার এলাকা৷"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"পিন অঞ্চল"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM পিন অঞ্চল"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK অঞ্চল"</string>
diff --git a/packages/Keyguard/res/values-bs-rBA/strings.xml b/packages/Keyguard/res/values-bs-rBA/strings.xml
index be73580..b8ff2a9 100644
--- a/packages/Keyguard/res/values-bs-rBA/strings.xml
+++ b/packages/Keyguard/res/values-bs-rBA/strings.xml
@@ -46,15 +46,10 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM kartica je zaključana."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM kartica je zaključana PUK kodom."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Otključavanje SIM kartice…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Otključavanje uzorkom."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Otključavanje pinom."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Otključavanje lozinkom."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Uzorak oblasti."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Oblast za pomjeranje klizača."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Prostor za PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Prostor za SIM PIN"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Prostor za SIM PUK"</string>
- <string name="keyguard_accessibility_next_alarm" msgid="7269583073750518672">"Naredni alarm je podešen za <xliff:g id="ALARM">%1$s</xliff:g>"</string>
+ <string name="keyguard_accessibility_next_alarm" msgid="7269583073750518672">"Sljedeći alarm je podešen za <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Izbriši"</string>
<string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Potvrdi"</string>
<string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Zaboravili ste uzorak?"</string>
diff --git a/packages/Keyguard/res/values-ca/strings.xml b/packages/Keyguard/res/values-ca/strings.xml
index b542866..9207e0e 100644
--- a/packages/Keyguard/res/values-ca/strings.xml
+++ b/packages/Keyguard/res/values-ca/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"La targeta SIM està bloquejada."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"La targeta SIM està bloquejada pel PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"S\'està desbloquejant la targeta SIM..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Desbloqueig mitjançant patró"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Desbloqueig mitjançant PIN"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Desbloqueig mitjançant contrasenya"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Àrea de patró"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Àrea per lliscar el dit"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Zona del PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Zona del PIN de la SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Zona del PUK de la SIM"</string>
diff --git a/packages/Keyguard/res/values-cs/strings.xml b/packages/Keyguard/res/values-cs/strings.xml
index b310323..aa7115d 100644
--- a/packages/Keyguard/res/values-cs/strings.xml
+++ b/packages/Keyguard/res/values-cs/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM karta je zablokována."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM karta je zablokována pomocí kódu PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Odblokování SIM karty…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Odemknutí gestem."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Odemknutí kódem PIN."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Odemknutí heslem."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Oblast pro zadání bezpečnostního gesta."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Oblast pro přejetí prstem."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Oblast kódu PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Oblast kódu PIN SIM karty"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Oblast kódu PUK SIM karty"</string>
diff --git a/packages/Keyguard/res/values-da/strings.xml b/packages/Keyguard/res/values-da/strings.xml
index b46b536..ebea6ee 100644
--- a/packages/Keyguard/res/values-da/strings.xml
+++ b/packages/Keyguard/res/values-da/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-kortet er låst."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-kort er låst med PUK-koden."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM-kortet låses op…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Lås op med mønster."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Lås op med pinkode."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Lås op med adgangskode."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Mønsterområde."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Strygeområde."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Område for pinkoden"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Område for pinkoden til simkortet"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Område for PUK-koden til simkortet"</string>
diff --git a/packages/Keyguard/res/values-de/strings.xml b/packages/Keyguard/res/values-de/strings.xml
index ae731c3..a519ce8 100644
--- a/packages/Keyguard/res/values-de/strings.xml
+++ b/packages/Keyguard/res/values-de/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-Karte ist gesperrt."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-Karte ist gesperrt. PUK-Eingabe erforderlich."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM-Karte wird entsperrt…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Entsperrung mit Muster"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Entsperrung mit PIN"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Entsperrung mit Passwort"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Bereich für Muster"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Bereich für Fingerbewegung"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN-Bereich"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM-PIN-Bereich"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM-PUK-Bereich"</string>
diff --git a/packages/Keyguard/res/values-el/strings.xml b/packages/Keyguard/res/values-el/strings.xml
index c54a7fc..d969266 100644
--- a/packages/Keyguard/res/values-el/strings.xml
+++ b/packages/Keyguard/res/values-el/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Η κάρτα SIM είναι κλειδωμένη."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Η κάρτα SIM είναι κλειδωμένη με κωδικό PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Ξεκλείδωμα κάρτας SIM…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Ξεκλείδωμα μοτίβου."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Ξεκλείδωμα κωδικού ασφαλείας"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Ξεκλείδωμα κωδικού πρόσβασης."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Περιοχή μοτίβου."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Περιοχή ολίσθησης"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Περιοχή PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Περιοχή PIN SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Περιοχή PUK SIM"</string>
diff --git a/packages/Keyguard/res/values-en-rAU/strings.xml b/packages/Keyguard/res/values-en-rAU/strings.xml
index e885166..9ecd979 100644
--- a/packages/Keyguard/res/values-en-rAU/strings.xml
+++ b/packages/Keyguard/res/values-en-rAU/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM card is locked."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM card is PUK-locked."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Unlocking SIM card…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Pattern unlock."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Pin unlock."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Password unlock."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Pattern area."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Slide area."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN area"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN area"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK area"</string>
diff --git a/packages/Keyguard/res/values-en-rGB/strings.xml b/packages/Keyguard/res/values-en-rGB/strings.xml
index e885166..9ecd979 100644
--- a/packages/Keyguard/res/values-en-rGB/strings.xml
+++ b/packages/Keyguard/res/values-en-rGB/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM card is locked."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM card is PUK-locked."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Unlocking SIM card…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Pattern unlock."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Pin unlock."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Password unlock."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Pattern area."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Slide area."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN area"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN area"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK area"</string>
diff --git a/packages/Keyguard/res/values-en-rIN/strings.xml b/packages/Keyguard/res/values-en-rIN/strings.xml
index e885166..9ecd979 100644
--- a/packages/Keyguard/res/values-en-rIN/strings.xml
+++ b/packages/Keyguard/res/values-en-rIN/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM card is locked."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM card is PUK-locked."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Unlocking SIM card…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Pattern unlock."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Pin unlock."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Password unlock."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Pattern area."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Slide area."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN area"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN area"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK area"</string>
diff --git a/packages/Keyguard/res/values-es-rUS/strings.xml b/packages/Keyguard/res/values-es-rUS/strings.xml
index df0db2d..61f5c0d 100644
--- a/packages/Keyguard/res/values-es-rUS/strings.xml
+++ b/packages/Keyguard/res/values-es-rUS/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"La tarjeta SIM está bloqueada."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"La tarjeta SIM está bloqueada por código PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Desbloqueando tarjeta SIM…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Desbloqueo por patrón"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Desbloqueo por PIN"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Desbloqueo por contraseña"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Área de patrón"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Área de deslizamiento"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Área de PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Área de PIN de SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Área de PUK de SIM"</string>
diff --git a/packages/Keyguard/res/values-es/strings.xml b/packages/Keyguard/res/values-es/strings.xml
index 6061b78..3ef737c 100644
--- a/packages/Keyguard/res/values-es/strings.xml
+++ b/packages/Keyguard/res/values-es/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"La tarjeta SIM está bloqueada."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"La tarjeta SIM está bloqueada con el código PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Desbloqueando tarjeta SIM…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Desbloqueo por patrón"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Desbloqueo por PIN"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Desbloqueo por contraseña"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Área de patrón"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Área para deslizar"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Área de PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Área de PIN de SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Área de PUK de SIM"</string>
diff --git a/packages/Keyguard/res/values-et-rEE/strings.xml b/packages/Keyguard/res/values-et-rEE/strings.xml
index 78ae3ca..47b6332 100644
--- a/packages/Keyguard/res/values-et-rEE/strings.xml
+++ b/packages/Keyguard/res/values-et-rEE/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-kaart on lukus."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-kaart on PUK-lukus."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM-kaardi avamine ..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Mustriga avamine."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"PIN-koodiga avamine."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Parooliga avamine."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Mustri ala."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Lohistamisala."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN-koodi ala"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM-kaardi PIN-koodi ala"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM-kaardi PUK-koodi ala"</string>
diff --git a/packages/Keyguard/res/values-eu-rES/strings.xml b/packages/Keyguard/res/values-eu-rES/strings.xml
index 7855b16..5f4abce 100644
--- a/packages/Keyguard/res/values-eu-rES/strings.xml
+++ b/packages/Keyguard/res/values-eu-rES/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM txartela blokeatuta dago."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM txartela PUK bidez blokeatuta dago."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM txartela desblokeatzen…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Ereduaren bidez desblokeatzea."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"PIN kodearen bidez desblokeatzea."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Pasahitzaren bidez desblokeatzea."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Eredua marrazteko eremua."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Hatza lerratzeko eremua."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN kodearen eremua"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM txartelaren PIN kodearen eremua"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM txartelaren PUK kodearen eremua"</string>
diff --git a/packages/Keyguard/res/values-fa/strings.xml b/packages/Keyguard/res/values-fa/strings.xml
index 39fd460..40952e2 100644
--- a/packages/Keyguard/res/values-fa/strings.xml
+++ b/packages/Keyguard/res/values-fa/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"سیم کارت قفل شد."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"سیم کارت با PUK قفل شده است."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"درحال بازگشایی قفل سیم کارت..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"باز کردن قفل با الگو."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"باز کردن قفل با پین."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"باز کردن قفل با گذرواژه."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"ناحیه الگو."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"ناحیه کشیدن انگشت روی صفحه."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"قسمت پین"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"قسمت پین سیمکارت"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"قسمت PUK سیمکارت"</string>
diff --git a/packages/Keyguard/res/values-fi/strings.xml b/packages/Keyguard/res/values-fi/strings.xml
index 7a0c00f..a1b96ca 100644
--- a/packages/Keyguard/res/values-fi/strings.xml
+++ b/packages/Keyguard/res/values-fi/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-kortti on lukittu."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-kortti on PUK-lukittu."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM-kortin lukitusta poistetaan…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Lukituksen poisto salasanalla."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Lukituksen poisto PIN-koodilla."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Lukituksen poisto salasanalla."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Kuvioalue."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Liu\'utusalue."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN-koodin alue"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM-kortin PIN-koodin alue"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM-kortin PUK-koodin alue"</string>
diff --git a/packages/Keyguard/res/values-fr-rCA/strings.xml b/packages/Keyguard/res/values-fr-rCA/strings.xml
index 9bc2456..d920415 100644
--- a/packages/Keyguard/res/values-fr-rCA/strings.xml
+++ b/packages/Keyguard/res/values-fr-rCA/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"La carte SIM est verrouillée."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"La carte SIM est verrouillée par clé PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Déverrouillage de la carte SIM en cours…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Déverrouillage par schéma"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Déverrouillage par NIP"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Déverrouillage par mot de passe"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Zone du schéma"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Zone où faire glisser votre doigt sur l\'écran"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Zone du NIP"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Zone du NIP de la carte SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Zone du code PUK de la carte SIM"</string>
diff --git a/packages/Keyguard/res/values-fr/strings.xml b/packages/Keyguard/res/values-fr/strings.xml
index ecb2575..8615b99 100644
--- a/packages/Keyguard/res/values-fr/strings.xml
+++ b/packages/Keyguard/res/values-fr/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"La carte SIM est verrouillée."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"La carte SIM est verrouillée par clé PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Déverrouillage de la carte SIM en cours…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Déverrouillage par schéma"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Déverrouillage par code PIN"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Déverrouillage par mot de passe"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Zone du schéma"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Zone où faire glisser votre doigt sur l\'écran"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Champ du code PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Champ du code PIN de la carte SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Champ du code PUK de la carte SIM"</string>
diff --git a/packages/Keyguard/res/values-gl-rES/strings.xml b/packages/Keyguard/res/values-gl-rES/strings.xml
index 382dd7b..a894fc5 100644
--- a/packages/Keyguard/res/values-gl-rES/strings.xml
+++ b/packages/Keyguard/res/values-gl-rES/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"A tarxeta SIM está bloqueada."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"A tarxeta SIM está bloqueada mediante un PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Desbloqueando tarxeta SIM…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Desbloqueo mediante padrón"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Desbloqueo mediante PIN"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Desbloqueo mediante contrasinal"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Zona do padrón"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Zona para pasar o dedo"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Área do PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Área do PIN da tarxeta SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Área do PUK da tarxeta SIM"</string>
diff --git a/packages/Keyguard/res/values-gu-rIN/strings.xml b/packages/Keyguard/res/values-gu-rIN/strings.xml
index eddd1a1..d288b3f 100644
--- a/packages/Keyguard/res/values-gu-rIN/strings.xml
+++ b/packages/Keyguard/res/values-gu-rIN/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM કાર્ડ લૉક કરેલ છે."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM કાર્ડ, PUK-લૉક કરેલ છે."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM કાર્ડ અનલૉક કરી રહ્યાં છે…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"પેટર્ન અનલૉક."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"પિન અનલૉક."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"પાસવર્ડ અનલૉક કરો."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"પેટર્ન ક્ષેત્ર."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"સ્લાઇડ ક્ષેત્ર."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN ક્ષેત્ર"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN ક્ષેત્ર"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK ક્ષેત્ર"</string>
diff --git a/packages/Keyguard/res/values-hi/strings.xml b/packages/Keyguard/res/values-hi/strings.xml
index 8069c5e..bf36312 100644
--- a/packages/Keyguard/res/values-hi/strings.xml
+++ b/packages/Keyguard/res/values-hi/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"सिम कार्ड लॉक है."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"सिम कार्ड PUK द्वारा लॉक किया हुआ है."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"सिम कार्ड अनलॉक हो रहा है…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"आकार अनलॉक."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"पिन अनलॉक."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"पासवर्ड अनलॉक."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"आकार क्षेत्र."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"स्लाइड क्षेत्र."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"पिन क्षेत्र"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"सिम पिन क्षेत्र"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"सिम पिइउके क्षेत्र"</string>
diff --git a/packages/Keyguard/res/values-hr/strings.xml b/packages/Keyguard/res/values-hr/strings.xml
index 044786d..169bc57 100644
--- a/packages/Keyguard/res/values-hr/strings.xml
+++ b/packages/Keyguard/res/values-hr/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM kartica je zaključana."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM kartica zaključana je PUK-om."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Otključavanje SIM kartice…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Uzorak za otključavanje."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Otključavanje PIN-om."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Otključavanje zaporkom."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Područje uzorka."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Područje klizanja."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Područje PIN-a"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Područje PIN-a za SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Područje PUK-a za SIM"</string>
diff --git a/packages/Keyguard/res/values-hu/strings.xml b/packages/Keyguard/res/values-hu/strings.xml
index e54a89f..bc3bf4e 100644
--- a/packages/Keyguard/res/values-hu/strings.xml
+++ b/packages/Keyguard/res/values-hu/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"A SIM kártya le van zárva."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"A SIM kártya le van zárva a PUK kóddal."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM kártya feloldása..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Feloldás mintával"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Feloldás PIN kóddal"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Feloldás jelszóval"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Mintaterület"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Csúsztatási terület"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN-kód területe"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN-kód területe"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK kód területe"</string>
diff --git a/packages/Keyguard/res/values-hy-rAM/strings.xml b/packages/Keyguard/res/values-hy-rAM/strings.xml
index d29a4ea..4aacf8f 100644
--- a/packages/Keyguard/res/values-hy-rAM/strings.xml
+++ b/packages/Keyguard/res/values-hy-rAM/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM քարտը կողպված է:"</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM քարտը PUK-ով կողպված է:"</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM քարտը ապակողպվում է..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Սխեմայով ապակողպում:"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Pin-ն ապակողպված է:"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Գաղտնաբառի ապակողպում:"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Սխեմայի տարածք:"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Սահեցման տարածք:"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN կոդի տարածք"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM քարտի PIN կոդի տարածք"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM քարտի PUK կոդի տարածք"</string>
@@ -109,7 +104,7 @@
<string name="kg_pin_accepted" msgid="1448241673570020097">"Կոդն ընդունվեց:"</string>
<string name="keyguard_carrier_default" msgid="8700650403054042153">"Ծառայություն չկա:"</string>
<string name="accessibility_ime_switch_button" msgid="2829803408288433429">"Փոխարկել մուտքագրման եղանակը"</string>
- <string name="airplane_mode" msgid="3122107900897202805">"Ինքնաթիռային ռեժիմ"</string>
+ <string name="airplane_mode" msgid="3122107900897202805">"Ինքնաթիռի ռեժիմ"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="5519822969283306009">"Սարքը վերագործարկելուց հետո անհրաժեշտ է մուտքագրել նախշը"</string>
<string name="kg_prompt_reason_restart_pin" msgid="4411398237158448198">"Սարքը վերագործարկելուց հետո անհրաժեշտ է մուտքագրել PIN կոդը"</string>
<string name="kg_prompt_reason_restart_password" msgid="6504585392626524695">"Սարքը վերագործարկելուց հետո անհրաժեշտ է մուտքագրել գաղտնաբառը"</string>
diff --git a/packages/Keyguard/res/values-in/strings.xml b/packages/Keyguard/res/values-in/strings.xml
index 6f8e7f1..dda63a8 100644
--- a/packages/Keyguard/res/values-in/strings.xml
+++ b/packages/Keyguard/res/values-in/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Kartu SIM terkunci."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Kartu SIM terkunci PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Membuka kartu SIM…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Buka kunci dengan pola."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Buka kunci dengan PIN."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Buka kunci dengan sandi."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Area pola."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Area geser."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Bidang PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Bidang PIN SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Bidang PUK SIM"</string>
diff --git a/packages/Keyguard/res/values-is-rIS/strings.xml b/packages/Keyguard/res/values-is-rIS/strings.xml
index 279ffcf..278e031 100644
--- a/packages/Keyguard/res/values-is-rIS/strings.xml
+++ b/packages/Keyguard/res/values-is-rIS/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-kortið er læst."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-kortið er PUK-læst."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Tekur SIM-kort úr lás…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Opnun með mynstri."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Opnun með PIN-númeri."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Opnun með aðgangsorði."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Svæði mynsturs."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Stroksvæði."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN-svæði"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"PIN-svæði SIM-korts"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"PUK-svæði SIM-korts"</string>
diff --git a/packages/Keyguard/res/values-it/strings.xml b/packages/Keyguard/res/values-it/strings.xml
index 728974d..98bcae4 100644
--- a/packages/Keyguard/res/values-it/strings.xml
+++ b/packages/Keyguard/res/values-it/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"La SIM è bloccata."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"La SIM è bloccata tramite PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Sblocco scheda SIM..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Sblocco con sequenza."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Sblocco con PIN."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Sblocco con password."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Area sequenza."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Area di scorrimento."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Area PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Area PIN SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Area PUK SIM"</string>
diff --git a/packages/Keyguard/res/values-iw/strings.xml b/packages/Keyguard/res/values-iw/strings.xml
index 47d0728..8d1ada3 100644
--- a/packages/Keyguard/res/values-iw/strings.xml
+++ b/packages/Keyguard/res/values-iw/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"כרטיס ה-SIM נעול."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"כרטיס SIM נעול באמצעות PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"מבטל נעילה של כרטיס SIM…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"ביטול נעילה באמצעות ציור קו."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"ביטול נעילה באמצעות מספר PIN."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"ביטול נעילה באמצעות סיסמה."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"אזור ציור קו."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"אזור הסטה."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"אזור עבור קוד PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"אזור עבור קוד PIN של SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"אזור עבור קוד PUK של SIM"</string>
diff --git a/packages/Keyguard/res/values-ja/strings.xml b/packages/Keyguard/res/values-ja/strings.xml
index d30355f..c4b4c98 100644
--- a/packages/Keyguard/res/values-ja/strings.xml
+++ b/packages/Keyguard/res/values-ja/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIMカードはロックされています。"</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIMカードはPUKでロックされています。"</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIMカードをロック解除しています…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"パターンロックを解除します。"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"PINロックを解除します。"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"パスワードロックを解除します。"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"パターンエリアです。"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"スライドエリアです。"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PINエリア"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PINエリア"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUKエリア"</string>
diff --git a/packages/Keyguard/res/values-ka-rGE/strings.xml b/packages/Keyguard/res/values-ka-rGE/strings.xml
index a398e00..658194ff 100644
--- a/packages/Keyguard/res/values-ka-rGE/strings.xml
+++ b/packages/Keyguard/res/values-ka-rGE/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM ბარათი დაბლოკილია."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM ბარათი დაბლოკილია PUK კოდით."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"მიმდინარეობს SIM ბარათის განბლოკვა…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"განბლოკვა ნიმუშით."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"განბლოკვა Pin-ით."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"პაროლის განბლოკვა"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"ნიმუშების სივრცე."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"გადასრიალების სივრცე."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN-ის არე"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM-ის PIN-ის არე"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM-ის PUK-ის არე"</string>
diff --git a/packages/Keyguard/res/values-kk-rKZ/strings.xml b/packages/Keyguard/res/values-kk-rKZ/strings.xml
index be261fb..2eb3948 100644
--- a/packages/Keyguard/res/values-kk-rKZ/strings.xml
+++ b/packages/Keyguard/res/values-kk-rKZ/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM картасы бекітулі."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM картасының PUK коды бекітілген."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM картасының бекітпесін ашуда…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Кескін арқылы ашу."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Pin арқылы ашу."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Құпия сөз арқылы ашу."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Кескін арқылы ашу аймағы."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Сырғыту аймағы."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN аумағы"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN аумағы"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK аумағы"</string>
diff --git a/packages/Keyguard/res/values-km-rKH/strings.xml b/packages/Keyguard/res/values-km-rKH/strings.xml
index 4d3f6e8..fce46c7 100644
--- a/packages/Keyguard/res/values-km-rKH/strings.xml
+++ b/packages/Keyguard/res/values-km-rKH/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"ស៊ីមកាតជាប់សោ។"</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"ស៊ីមកាតជាប់កូដ PUK ។"</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"កំពុងដោះសោស៊ីមកាត..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"លំនាំដោះសោ។"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"កូដ PIN ដោះសោ។"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"ពាក្យសម្ងាត់ដោះសោ។"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"ផ្ទៃលំនាំ។"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"ផ្ទៃរុញ។"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"ប្រអប់លេខសម្ងាត់"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"ប្រអប់លេខសម្ងាត់ស៊ីម"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"ប្រអប់ PUK ស៊ីម"</string>
diff --git a/packages/Keyguard/res/values-kn-rIN/strings.xml b/packages/Keyguard/res/values-kn-rIN/strings.xml
index 5691a63..7bac9c6 100644
--- a/packages/Keyguard/res/values-kn-rIN/strings.xml
+++ b/packages/Keyguard/res/values-kn-rIN/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"ಸಿಮ್ ಕಾರ್ಡ್ ಲಾಕ್ ಮಾಡಲಾಗಿದೆ."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"ಸಿಮ್ ಕಾರ್ಡ್ ಅನ್ನು PUK-ಲಾಕ್ ಮಾಡಲಾಗಿದೆ."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"ಸಿಮ್ ಕಾರ್ಡ್ ಅನ್ಲಾಕ್ ಮಾಡಲಾಗುತ್ತಿದೆ…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"ಪ್ಯಾಟರ್ನ್ ಅನ್ಲಾಕ್."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"ಪಿನ್ ಅನ್ಲಾಕ್."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"ಪಾಸ್ವರ್ಡ್ ಅನ್ಲಾಕ್."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"ಪ್ಯಾಟರ್ನ್ ಪ್ರದೇಶ."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"ಸ್ಲೈಡ್ ಪ್ರದೇಶ."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"ಪಿನ್ ಪ್ರದೇಶ"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"ಸಿಮ್ ಪಿನ್ ಪ್ರದೇಶ"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"ಸಿಮ್ PUK ಪ್ರದೇಶ"</string>
diff --git a/packages/Keyguard/res/values-ko/strings.xml b/packages/Keyguard/res/values-ko/strings.xml
index 2eeef2b..5d40d4d 100644
--- a/packages/Keyguard/res/values-ko/strings.xml
+++ b/packages/Keyguard/res/values-ko/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM 카드가 잠겨 있습니다."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM 카드가 PUK 잠김 상태입니다."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM 카드 잠금해제 중..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"패턴을 사용하여 잠금해제합니다."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"핀을 사용하여 잠금해제합니다."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"비밀번호를 사용하여 잠금해제합니다."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"패턴을 그리는 부분입니다."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"슬라이드하는 부분입니다."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN 영역"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN 영역"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK 영역"</string>
diff --git a/packages/Keyguard/res/values-ky-rKG/strings.xml b/packages/Keyguard/res/values-ky-rKG/strings.xml
index d42b1fa..a485528 100644
--- a/packages/Keyguard/res/values-ky-rKG/strings.xml
+++ b/packages/Keyguard/res/values-ky-rKG/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-карта бөгөттөлгөн."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-карта PUK-бөгөттө."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM-карта бөгөттөн чыгарылууда…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Үлгү менен ачуу."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Пин код менен ачуу."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Сырсөз менен ачуу."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Үлгү аймагы."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Жылмыштыруу аймагы."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN аймагы"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN аймагы"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK аймагы"</string>
diff --git a/packages/Keyguard/res/values-lo-rLA/strings.xml b/packages/Keyguard/res/values-lo-rLA/strings.xml
index 8e7c813..29a1b56 100644
--- a/packages/Keyguard/res/values-lo-rLA/strings.xml
+++ b/packages/Keyguard/res/values-lo-rLA/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"ຊິມກາດຖືກລັອກ."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"ຊິມກາດຖືກລັອກດ້ວຍລະຫັດ PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"ກຳລັງປົດລັອກຊິມກາດ..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"ປົດລັອກດ້ວຍຮູບແບບ."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"ປົດລັອກດ້ວຍ PIN."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"ການປົດລັອກດ້ວຍລະຫັດຜ່ານ."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"ພື້ນທີ່ຮູບແບບ."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"ເລື່ອນພື້ນທີ່."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"ພື້ນທີ່ PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"ພື້ນທີ່ PIN ຂອງ SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"ພື້ນທີ່ PUK ຂອງ SIM"</string>
diff --git a/packages/Keyguard/res/values-lt/strings.xml b/packages/Keyguard/res/values-lt/strings.xml
index 653c089..fd41efc 100644
--- a/packages/Keyguard/res/values-lt/strings.xml
+++ b/packages/Keyguard/res/values-lt/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM kortelė užrakinta."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM kortelė užrakinta PUK kodu."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Atrakinama SIM kortelė…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Atrakinimas pagal piešinį."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Atrakinimas įvedus PIN kodą."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Atrakinimas įvedus slaptažodį."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Atrakinimo pagal piešinį sritis."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Slydimo sritis."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN kodo sritis"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM kortelės PIN kodo sritis"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM kortelės PUK kodo sritis"</string>
diff --git a/packages/Keyguard/res/values-lv/strings.xml b/packages/Keyguard/res/values-lv/strings.xml
index 449659e..f801d64 100644
--- a/packages/Keyguard/res/values-lv/strings.xml
+++ b/packages/Keyguard/res/values-lv/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM karte ir bloķēta."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM karte ir bloķēta ar PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Notiek SIM kartes atbloķēšana..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Autorizācija ar kombināciju."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Autorizācija ar PIN kodu."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Autorizācija ar paroli."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Kombinācijas ievades apgabals."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Apgabals, kur vilkt ar pirkstu."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN apgabals"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN apgabals"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK apgabals"</string>
diff --git a/packages/Keyguard/res/values-mk-rMK/strings.xml b/packages/Keyguard/res/values-mk-rMK/strings.xml
index a6ee921..9d833f0 100644
--- a/packages/Keyguard/res/values-mk-rMK/strings.xml
+++ b/packages/Keyguard/res/values-mk-rMK/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"СИМ картичката е заклучена."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"СИМ картичката е заклучена со ПУК."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"СИМ картичката се отклучува..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Отклучување со шема."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Отклучување со пин."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Отклучување со лозинка."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Област за шема."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Област за лизгање."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Поле за PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Поле за PIN на СИМ"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Поле за ПУК на СИМ"</string>
diff --git a/packages/Keyguard/res/values-ml-rIN/strings.xml b/packages/Keyguard/res/values-ml-rIN/strings.xml
index fa39ae1..5d93cf0 100644
--- a/packages/Keyguard/res/values-ml-rIN/strings.xml
+++ b/packages/Keyguard/res/values-ml-rIN/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"സിം കാർഡ് ലോക്കുചെയ്തു."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"സിം കാർഡ് PUK-ലോക്ക് ചെയ്തതാണ്."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"സിം കാർഡ് അൺലോക്കുചെയ്യുന്നു…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"പാറ്റേൺ അൺലോക്ക്."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"പിൻ അൺലോക്ക്."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"പാസ്വേഡ് അൺലോക്ക്."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"പാറ്റേൺ ഏരിയ."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"സ്ലൈഡ് ഏരിയ."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN ഏരിയ"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN ഏരിയ"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK ഏരിയ"</string>
diff --git a/packages/Keyguard/res/values-mn-rMN/strings.xml b/packages/Keyguard/res/values-mn-rMN/strings.xml
index d9014c8..8641e31 100644
--- a/packages/Keyguard/res/values-mn-rMN/strings.xml
+++ b/packages/Keyguard/res/values-mn-rMN/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM карт түгжигдсэн."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM картны PUK-түгжигдсэн."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM картны түгжээг гаргаж байна…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Тайлах хээ."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Тайлах пин."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Тайлах нууц үг."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Хээний хэсэг."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Гулсуулах хэсэг."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN талбар"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN талбар"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK талбар"</string>
@@ -93,7 +88,7 @@
<string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4686386497449912146">"Та таблетын түгжээг тайлах оролдлогыг <xliff:g id="NUMBER">%d</xliff:g> удаа буруу оруулсан байна. Ажлын профайл устгагдаж, улмаар профайлын бүх мэдээлэл устах болно."</string>
<string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4951507352869831265">"Та утасны түгжээг тайлах оролдлогыг <xliff:g id="NUMBER">%d</xliff:g> удаа буруу оруулсан байна. Ажлын профайл устгагдаж, улмаар профайлын бүх мэдээлэл устах болно."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Та тайлах хээг <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу зурлаа. <xliff:g id="NUMBER_1">%2$d</xliff:g> удаа дахин буруу оруулбал, та таблетаа тайлахын тулд имэйл бүртгэл шаардлагатай болно.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундын дараа дахин оролдоно уу."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Та тайлах хээг <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу зурлаа. <xliff:g id="NUMBER_1">%2$d</xliff:g> удаа дахин буруу оруулбал, та утсаа тайлахын тулд имэйл акаунтаа ашиглах шаардлагатай болно.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундын дараа дахин оролдоно уу."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Та тайлах хээг <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу зурлаа. <xliff:g id="NUMBER_1">%2$d</xliff:g> удаа дахин буруу оруулбал, та утсаа тайлахын тулд имэйл бүртгэлээ ашиглах шаардлагатай болно.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундын дараа дахин оролдоно уу."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="30531039455764924">"СИМ ПИН код буруу, та төхөөрөмжийн түгжээг тайлахын тулд оператор компанитай холбоо барих шаардлагатай."</string>
<plurals name="kg_password_wrong_pin_code" formatted="false" msgid="6721575017538162249">
<item quantity="other">СИМ-ны ПИН код буруу байна. Та <xliff:g id="NUMBER_1">%d</xliff:g> удаа оролдлого хийх боломжтой байна.</item>
diff --git a/packages/Keyguard/res/values-mr-rIN/strings.xml b/packages/Keyguard/res/values-mr-rIN/strings.xml
index 57a95be..8bcaad6 100644
--- a/packages/Keyguard/res/values-mr-rIN/strings.xml
+++ b/packages/Keyguard/res/values-mr-rIN/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"सिम कार्ड लॉक झाले आहे."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"सिम कार्ड PUK-लॉक केलेले आहे."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"सिम कार्ड अनलॉक करत आहे…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"नमुना अनलॉक."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"पिन अनलॉक."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"संकेतशब्द अनलॉक."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"नमुना क्षेत्र."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"स्लाइड क्षेत्र."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"पिन क्षेत्र"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"सिम पिन क्षेत्र"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"सिम PUK क्षेत्र"</string>
diff --git a/packages/Keyguard/res/values-ms-rMY/strings.xml b/packages/Keyguard/res/values-ms-rMY/strings.xml
index 803d40a..b7b093f 100644
--- a/packages/Keyguard/res/values-ms-rMY/strings.xml
+++ b/packages/Keyguard/res/values-ms-rMY/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Kad SIM dikunci."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Kad SIM dikunci dengan PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Membuka kunci kad SIM..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Buka kunci corak."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Buka kunci pin."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Buka kunci kata laluan."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Kawasan corak."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Kawasan luncur."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Kawasan PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Kawasan PIN SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Kawasan PUK SIM"</string>
diff --git a/packages/SystemUI/res/drawable/notification_header_bg.xml b/packages/Keyguard/res/values-my-rMM/dimens.xml
similarity index 70%
rename from packages/SystemUI/res/drawable/notification_header_bg.xml
rename to packages/Keyguard/res/values-my-rMM/dimens.xml
index 1f46502..21b2a46 100644
--- a/packages/SystemUI/res/drawable/notification_header_bg.xml
+++ b/packages/Keyguard/res/values-my-rMM/dimens.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2014 The Android Open Source Project
+ ~ 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.
@@ -14,8 +14,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="?android:attr/colorControlHighlight" >
- <item android:drawable="@color/system_secondary_color"/>
-</ripple>
\ No newline at end of file
+<resources>
+ <dimen name="bottom_text_spacing_digital">4dp</dimen>
+</resources>
diff --git a/packages/Keyguard/res/values-my-rMM/strings.xml b/packages/Keyguard/res/values-my-rMM/strings.xml
index 5db7e95..7a7664e 100644
--- a/packages/Keyguard/res/values-my-rMM/strings.xml
+++ b/packages/Keyguard/res/values-my-rMM/strings.xml
@@ -46,16 +46,11 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"ဆင်းမ်ကဒ် သော့ကျနေပါသည်"</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"ဆင်းမ်ကဒ် ရဲ့ ပင်နံပါတ် ပြန်ဖွင့်သည့် ကုဒ် သော့ကျနေပါသည်"</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"ဆင်းမ်ကဒ် ကို သော့ဖွင့်နေပါသည်"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"ပုံစံဖြင့် သော့ဖွင့်ခြင်း"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"ပင်နံပါတ်ဖြင့် သော့ဖွင့်ခြင်း"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"စကားဝှက်ဖြင့် သော့ဖွင့်ခြင်း"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"ပုံစံနေရာ"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"ဘေးတိုက်ပွတ်ဆွဲရန် နေရာ"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN နေရာ"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN နေရာ"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK နေရာ"</string>
<string name="keyguard_accessibility_next_alarm" msgid="7269583073750518672">"<xliff:g id="ALARM">%1$s</xliff:g> အတွက် နောက် သတိပေးရန် သတ်မှတ်ချက်"</string>
- <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"ဖျက်ရန်ခလုတ်"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"ဖျက်ရန်"</string>
<string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enterခလုတ်"</string>
<string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"ပုံဖော်မှုအား မေ့လျော့ခြင်း"</string>
<string name="kg_wrong_pattern" msgid="1850806070801358830">"ပုံဆွဲအမှား"</string>
diff --git a/packages/Keyguard/res/values-nb/strings.xml b/packages/Keyguard/res/values-nb/strings.xml
index 1bdf444..e0035da 100644
--- a/packages/Keyguard/res/values-nb/strings.xml
+++ b/packages/Keyguard/res/values-nb/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-kortet er låst."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-kortet er PUK-låst."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Låser opp SIM-kortet ..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Mønsteropplåsning."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"PIN-opplåsning."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Passordopplåsning."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Mønsterområde."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Dra-felt."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN-området"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"PIN-området for SIM-kortet"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"PUK-området for SIM-kortet"</string>
diff --git a/packages/Keyguard/res/values-ne-rNP/strings.xml b/packages/Keyguard/res/values-ne-rNP/strings.xml
index 2cf863d..47f5432 100644
--- a/packages/Keyguard/res/values-ne-rNP/strings.xml
+++ b/packages/Keyguard/res/values-ne-rNP/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM कार्ड लक गरियो।"</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM कार्ड PUK-लक छ।"</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM कार्ड अनलक हुँदै…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"ढाँचा अनलक।"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Pin अनलक"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"पासवर्ड अनलक।"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"ढाँचा क्षेत्र।"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"स्लाइड क्षेत्र।"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"पीन क्षेत्र"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM पिन क्षेत्र"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM पुक क्षेत्र"</string>
diff --git a/packages/Keyguard/res/values-nl/strings.xml b/packages/Keyguard/res/values-nl/strings.xml
index a33165d..c44381e 100644
--- a/packages/Keyguard/res/values-nl/strings.xml
+++ b/packages/Keyguard/res/values-nl/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Simkaart is vergrendeld."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Simkaart is vergrendeld met PUK-code."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Simkaart ontgrendelen…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Ontgrendeling via patroon."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Ontgrendeling via pincode."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Ontgrendeling via wachtwoord."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Tekengebied voor patroon."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Schuifgebied."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Gebied voor pincode"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Gebied voor sim-pincode"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Gebied voor sim-pukcode"</string>
diff --git a/packages/Keyguard/res/values-pa-rIN/strings.xml b/packages/Keyguard/res/values-pa-rIN/strings.xml
index 8cf86a0..16ca29c 100644
--- a/packages/Keyguard/res/values-pa-rIN/strings.xml
+++ b/packages/Keyguard/res/values-pa-rIN/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM ਕਾਰਡ ਲੌਕ ਕੀਤਾ ਹੋਇਆ ਹੈ।"</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM ਕਾਰਡ PUK-ਲੌਕਡ ਹੈ।"</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM ਕਾਰਡ ਅਨਲੌਕ ਕਰ ਰਿਹਾ ਹੈ…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"ਪੈਟਰਨ ਅਨਲੌਕ।"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Pin ਅਨਲੌਕ।"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"ਪਾਸਵਰਡ ਅਨਲੌਕ।"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"ਪੈਟਰਨ ਖੇਤਰ।"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"ਖੇਤਰ ਸਲਾਈਡ ਕਰੋ।"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN ਖੇਤਰ"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN ਖੇਤਰ"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK ਖੇਤਰ"</string>
diff --git a/packages/Keyguard/res/values-pl/strings.xml b/packages/Keyguard/res/values-pl/strings.xml
index ee20850..f0980da 100644
--- a/packages/Keyguard/res/values-pl/strings.xml
+++ b/packages/Keyguard/res/values-pl/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Karta SIM jest zablokowana."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Karta SIM jest zablokowana za pomocą kodu PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Odblokowuję kartę SIM…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Odblokowanie wzorem."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Odblokowanie kodem PIN."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Odblokowanie hasłem."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Obszar wzoru."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Obszar przesuwania."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Miejsce na PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Miejsce na PIN do karty SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Miejsce na PUK do karty SIM"</string>
diff --git a/packages/Keyguard/res/values-pt-rBR/strings.xml b/packages/Keyguard/res/values-pt-rBR/strings.xml
index d7215c1..2663337 100644
--- a/packages/Keyguard/res/values-pt-rBR/strings.xml
+++ b/packages/Keyguard/res/values-pt-rBR/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"O cartão SIM está bloqueado."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"O cartão SIM está bloqueado pelo PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Desbloqueando o cartão SIM…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Desbloqueio com padrão."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Desbloqueio com PIN."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Desbloqueio com senha."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Área do padrão."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Área de deslize."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Área do PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Área do PIN SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Área do PUK SIM"</string>
diff --git a/packages/Keyguard/res/values-pt-rPT/strings.xml b/packages/Keyguard/res/values-pt-rPT/strings.xml
index f0b2bbc..e417e07 100644
--- a/packages/Keyguard/res/values-pt-rPT/strings.xml
+++ b/packages/Keyguard/res/values-pt-rPT/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"O cartão SIM está bloqueado."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"O cartão SIM está bloqueado por PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"A desbloquear o cartão SIM..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Desbloqueio através de sequência."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Desbloqueio através de PIN."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Desbloqueio através de palavra-passe."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Área da sequência."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Área de deslize."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Área do PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Área do PIN do SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Área do PUK do SIM"</string>
diff --git a/packages/Keyguard/res/values-pt/strings.xml b/packages/Keyguard/res/values-pt/strings.xml
index d7215c1..2663337 100644
--- a/packages/Keyguard/res/values-pt/strings.xml
+++ b/packages/Keyguard/res/values-pt/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"O cartão SIM está bloqueado."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"O cartão SIM está bloqueado pelo PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Desbloqueando o cartão SIM…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Desbloqueio com padrão."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Desbloqueio com PIN."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Desbloqueio com senha."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Área do padrão."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Área de deslize."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Área do PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Área do PIN SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Área do PUK SIM"</string>
diff --git a/packages/Keyguard/res/values-ro/strings.xml b/packages/Keyguard/res/values-ro/strings.xml
index ea5380c..09a066a 100644
--- a/packages/Keyguard/res/values-ro/strings.xml
+++ b/packages/Keyguard/res/values-ro/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Cardul SIM este blocat."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Cardul SIM este blocat cu codul PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Se deblochează cardul SIM…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Deblocare cu model."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Deblocare cu PIN."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Deblocare cu parolă."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Zonă model."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Zonă glisare."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Zona codului PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Zona codului PIN al cardului SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Zona codului PUK al cardului SIM"</string>
@@ -61,7 +56,7 @@
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Model greșit"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Parolă greșită"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Cod PIN greșit"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Încercați din nou peste <xliff:g id="NUMBER">%d</xliff:g> (de) secunde."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Încercați din nou peste <xliff:g id="NUMBER">%d</xliff:g> secunde."</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Desenați modelul"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Introduceți codul PIN al cardului SIM"</string>
<string name="kg_sim_pin_instructions_multi" msgid="7818515973197201434">"Introduceți codul PIN al cardului SIM pentru „<xliff:g id="CARRIER">%1$s</xliff:g>”"</string>
@@ -77,9 +72,9 @@
<string name="kg_invalid_puk" msgid="3638289409676051243">"Reintroduceți codul PUK corect. Încercările repetate vor dezactiva definitiv cardul SIM."</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Codurile PIN nu coincid"</string>
<string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Prea multe încercări de desenare a modelului"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Ați introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> (de) secunde."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Ați introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> (de) secunde."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> (de) secunde."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Ați introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Ați introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="8774056606869646621">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, această tabletă va fi resetată, iar toate datele acesteia vor fi șterse."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="1843331751334128428">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acest telefon va fi resetat, iar toate datele acestuia vor fi șterse."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="258925501999698032">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Această tabletă va fi resetată, iar toate datele acesteia vor fi șterse."</string>
@@ -92,8 +87,8 @@
<string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="6853071165802933545">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, profilul de serviciu va fi eliminat, iar toate datele profilului vor fi șterse."</string>
<string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4686386497449912146">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Profilul de serviciu va fi eliminat, iar toate datele profilului vor fi șterse."</string>
<string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4951507352869831265">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Profilul de serviciu va fi eliminat, iar toate datele profilului vor fi șterse."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați tableta cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> (de) secunde."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați telefonul cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> (de) secunde."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați tableta cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați telefonul cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="30531039455764924">"Codul PIN pentru cardul SIM este incorect. Contactați operatorul pentru a vă debloca dispozitivul."</string>
<plurals name="kg_password_wrong_pin_code" formatted="false" msgid="6721575017538162249">
<item quantity="few">Codul PIN pentru cardul SIM este incorect. V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> încercări.</item>
diff --git a/packages/Keyguard/res/values-ru/strings.xml b/packages/Keyguard/res/values-ru/strings.xml
index 264e42c..7466c66 100644
--- a/packages/Keyguard/res/values-ru/strings.xml
+++ b/packages/Keyguard/res/values-ru/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-карта заблокирована"</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Для разблокировки SIM-карты требуется PUK-код."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Разблокировка SIM-карты…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Графический ключ"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"PIN-код"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Пароль"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Область ввода графического ключа"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Область слайдера"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN-код"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"PIN-код SIM-карты"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"PUK-код SIM-карты"</string>
diff --git a/packages/Keyguard/res/values-si-rLK/strings.xml b/packages/Keyguard/res/values-si-rLK/strings.xml
index 607e8ac..5f96e8c 100644
--- a/packages/Keyguard/res/values-si-rLK/strings.xml
+++ b/packages/Keyguard/res/values-si-rLK/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM පත අගුළු දමා ඇත."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM පත PUK අගුළු ලා ඇත."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM පත අගුළු හරිමින්..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"රටා අගුළු ඇරීම."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"PIN අගුළු ඇරීම."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"මුරපද අගුළු ඇරීම."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"රටා ප්රදේශය."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"සර්පණ ප්රදේශය."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN කොටස"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN කොටස"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK කොටස"</string>
diff --git a/packages/Keyguard/res/values-sk/strings.xml b/packages/Keyguard/res/values-sk/strings.xml
index 4cbfd51..82a4f1d4 100644
--- a/packages/Keyguard/res/values-sk/strings.xml
+++ b/packages/Keyguard/res/values-sk/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM karta je uzamknutá."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM karta je uzamknutá pomocou kódu PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Prebieha odomykanie SIM karty..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Odomknutie vzorom."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Odomknutie kódom PIN."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Odomknutie heslom."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Oblasť na zadanie bezpečnostného vzoru."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Oblasť na prejdenie prstom."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Oblasť kódu PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Oblasť kódu PIN SIM karty"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Oblasť kódu PUK SIM karty"</string>
diff --git a/packages/Keyguard/res/values-sl/strings.xml b/packages/Keyguard/res/values-sl/strings.xml
index 91083f5..9100bd3 100644
--- a/packages/Keyguard/res/values-sl/strings.xml
+++ b/packages/Keyguard/res/values-sl/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Kartica SIM je zaklenjena."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Kartica SIM je zaklenjena s kodo PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Odklepanje kartice SIM …"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Odklepanje z vzorcem."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Odklepanje s kodo PIN."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Odklepanje z geslom."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Območje vzorca."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Območje podrsanja."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Območje za kodo PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Območje za kodo PIN za SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Območje za kodo PUK za SIM"</string>
diff --git a/packages/Keyguard/res/values-sq-rAL/strings.xml b/packages/Keyguard/res/values-sq-rAL/strings.xml
index 7f4becb..31d31b7 100644
--- a/packages/Keyguard/res/values-sq-rAL/strings.xml
+++ b/packages/Keyguard/res/values-sq-rAL/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Karta SIM është e kyçur."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Karta SIM është e kyçur me PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Po shkyç kartën SIM…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Shkyçje me motiv."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Shkyçje me PIN."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Shkyçja e fjalëkalimit."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Zona e motivit."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Zonën e rrëshqitjes."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Zona PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Zona PIN e kartës SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Zona e PUK-ut të kartës SIM"</string>
diff --git a/packages/Keyguard/res/values-sr/strings.xml b/packages/Keyguard/res/values-sr/strings.xml
index 62614a6..840cae7 100644
--- a/packages/Keyguard/res/values-sr/strings.xml
+++ b/packages/Keyguard/res/values-sr/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM картица је закључана."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM картица је закључана PUK кодом."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Откључавање SIM картице…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Откључавање шаблоном."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Откључавање PIN-ом."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Откључавање лозинком."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Област шаблона."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Област превлачења."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Област за PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Област за PIN за SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Област за PUK за SIM"</string>
diff --git a/packages/Keyguard/res/values-sv/strings.xml b/packages/Keyguard/res/values-sv/strings.xml
index 378f047..527c8e6 100644
--- a/packages/Keyguard/res/values-sv/strings.xml
+++ b/packages/Keyguard/res/values-sv/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-kortet är låst."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-kortet är PUK-låst."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Låser upp SIM-kort …"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Lås upp med grafiskt lösenord."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Lås upp med PIN-kod."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Lås upp med lösenord."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Fält för grafiskt lösenord."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Fält med dragreglage."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Pinkodsområde"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Pinkodsområde för SIM-kort"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"PUK-kodsområde för SIM-kort"</string>
diff --git a/packages/Keyguard/res/values-sw/strings.xml b/packages/Keyguard/res/values-sw/strings.xml
index b570e9f..c2e7ac9 100644
--- a/packages/Keyguard/res/values-sw/strings.xml
+++ b/packages/Keyguard/res/values-sw/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM kadi imefungwa."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM kadi imefungwa na PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Inafungua SIM kadi..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Kufungua kwa ruwaza."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Kufungua kwa PIN."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Kufungua kwa nenosiri."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Eneo la ruwaza."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Eneo la slaidi."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Eneo la PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Eneo la PIN ya SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Eneo la PUK ya SIM"</string>
diff --git a/packages/Keyguard/res/values-ta-rIN/strings.xml b/packages/Keyguard/res/values-ta-rIN/strings.xml
index 2e3f588..c80ddce 100644
--- a/packages/Keyguard/res/values-ta-rIN/strings.xml
+++ b/packages/Keyguard/res/values-ta-rIN/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"சிம் கார்டு பூட்டப்பட்டுள்ளது."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"சிம் கார்டு PUK ஆல் பூட்டப்பட்டது."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"சிம் கார்டின் தடையைநீக்குகிறது..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"வடிவம் மூலம் திறத்தல்."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Pin மூலம் திறத்தல்."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"கடவுச்சொல் மூலம் திறத்தல்."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"வடிவப் பகுதி."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"ஸ்லைடு பகுதி."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN பகுதி"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"சிம் PIN பகுதி"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"சிம் PUK பகுதி"</string>
diff --git a/packages/Keyguard/res/values-te-rIN/strings.xml b/packages/Keyguard/res/values-te-rIN/strings.xml
index 6c521aa..a72a85b 100644
--- a/packages/Keyguard/res/values-te-rIN/strings.xml
+++ b/packages/Keyguard/res/values-te-rIN/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"సిమ్ కార్డు లాక్ చేయబడింది."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"సిమ్ కార్డు PUK లాక్ చేయబడింది."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"సిమ్ కార్డును అన్లాక్ చేస్తోంది…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"నమూనా అన్లాక్."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"పిన్ అన్లాక్."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"పాస్వర్డ్ అన్లాక్."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"నమూనా ప్రాంతం."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"స్లయిడ్ ప్రాంతం."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN ప్రాంతం"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN ప్రాంతం"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK ప్రాంతం"</string>
diff --git a/packages/Keyguard/res/values-th/strings.xml b/packages/Keyguard/res/values-th/strings.xml
index 1799fe8..e094d35 100644
--- a/packages/Keyguard/res/values-th/strings.xml
+++ b/packages/Keyguard/res/values-th/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"ซิมการ์ดถูกล็อก"</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"ซิมการ์ดถูกล็อกด้วย PUK"</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"กำลังปลดล็อกซิมการ์ด…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"การปลดล็อกด้วยรูปแบบ"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"การปลดล็อกด้วย PIN"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"การปลดล็อกด้วยรหัสผ่าน"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"พื้นที่สำหรับรูปแบบ"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"พื้นที่สำหรับการเลื่อน"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"พื้นที่ PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"พื้นที่ PIN ของซิม"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"พื้นที่ PUK ของซิม"</string>
diff --git a/packages/Keyguard/res/values-tl/strings.xml b/packages/Keyguard/res/values-tl/strings.xml
index 39cad72..73492e2 100644
--- a/packages/Keyguard/res/values-tl/strings.xml
+++ b/packages/Keyguard/res/values-tl/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Naka-lock ang SIM card."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Naka-lock ang SIM card gamit ang PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Ina-unlock ang SIM card…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Pag-unlock ng pattern."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Pag-unlock ng pin."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Pag-unlock ng password."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Bahagi ng pattern."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Bahagi ng slide."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Lugar ng PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Lugar ng PIN ng SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Lugar ng PUK ng SIM"</string>
diff --git a/packages/Keyguard/res/values-tr/strings.xml b/packages/Keyguard/res/values-tr/strings.xml
index ffcbd12..3ef0705 100644
--- a/packages/Keyguard/res/values-tr/strings.xml
+++ b/packages/Keyguard/res/values-tr/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM kart kilitli."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM kart PUK kilidi devrede."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM kart kilidi açılıyor…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Desenle kilit açma."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Pin koduyla kilit açma."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Şifreyle kilit açma."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Desen alanı."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Kaydırma alanı."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN alanı"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN alanı"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK alanı"</string>
diff --git a/packages/Keyguard/res/values-uk/strings.xml b/packages/Keyguard/res/values-uk/strings.xml
index be19281..a508689 100644
--- a/packages/Keyguard/res/values-uk/strings.xml
+++ b/packages/Keyguard/res/values-uk/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM-карту заблоковано."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM-карту заблоковано PUK-кодом."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Розблокування SIM-карти…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Розблокування ключем."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Розблокування PIN-кодом."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Розблокування паролем."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Область ключа."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Область повзунка."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN-код"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"PIN-код SIM-карти"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"PUK-код SIM-карти"</string>
diff --git a/packages/Keyguard/res/values-ur-rPK/strings.xml b/packages/Keyguard/res/values-ur-rPK/strings.xml
index 63f4e85..1070d58 100644
--- a/packages/Keyguard/res/values-ur-rPK/strings.xml
+++ b/packages/Keyguard/res/values-ur-rPK/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM کارڈ مقفل ہے۔"</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM کارڈ PUK-مقفل ہے۔"</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM کارڈ غیر مقفل کیا جا رہا ہے…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"پیٹرن کے ذریعے غیر مقفل کریں۔"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"پن کے ذریعے غیر مقفل کریں۔"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"پاس ورڈ کے ذریعہ غیر مقفل کریں۔"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"پیٹرن کا علاقہ۔"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"سلائیڈ کرنے کا علاقہ۔"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN کا علاقہ"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN کا علاقہ"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK کا علاقہ"</string>
diff --git a/packages/Keyguard/res/values-uz-rUZ/strings.xml b/packages/Keyguard/res/values-uz-rUZ/strings.xml
index a353d12..a9df331 100644
--- a/packages/Keyguard/res/values-uz-rUZ/strings.xml
+++ b/packages/Keyguard/res/values-uz-rUZ/strings.xml
@@ -46,19 +46,14 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM karta qulflangan."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM karta PUK kod bilan qulflangan."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"SIM karta qulfi ochilmoqda…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Chizmali qulfni ochish."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Pin qulfini ochish."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Parolli qulfni ochish."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Chizmali qulf maydoni."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Maydonni silang"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN-kod maydoni"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM karta PIN kodi maydoni"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM karta PUK kodi maydoni"</string>
- <string name="keyguard_accessibility_next_alarm" msgid="7269583073750518672">"Uyg‘otkich signali <xliff:g id="ALARM">%1$s</xliff:g> da chalinadi."</string>
+ <string name="keyguard_accessibility_next_alarm" msgid="7269583073750518672">"Signal <xliff:g id="ALARM">%1$s</xliff:g> da chalinadi."</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"O‘chirish"</string>
<string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Kiritish"</string>
- <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Chizmali parol unutilgan"</string>
- <string name="kg_wrong_pattern" msgid="1850806070801358830">"Chizmali kalit noto‘g‘ri"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Grafik kalit esimdan chiqdi"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Grafik kalit noto‘g‘ri"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Parol noto‘g‘ri"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"PIN-kod noto‘g‘ri"</string>
<string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"<xliff:g id="NUMBER">%d</xliff:g> soniyadan so‘ng qayta urinib ko‘ring."</string>
@@ -76,10 +71,10 @@
<string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK kod kamida 8 ta raqam bo‘lishi shart."</string>
<string name="kg_invalid_puk" msgid="3638289409676051243">"To‘g‘ri PUK kodni qayta kiriting. Qayta-qayta urinishlar SIM kartani butunlay o‘chirib qo‘yadi."</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN-kod mos kelmadi"</string>
- <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Chizmali parolni ochishga juda ko‘p urinildi"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Grafik kalit juda ko‘p marta chizildi"</string>
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Siz PIN-kodni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri kiritdingiz. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> soniyadan so‘ng qayta urinib ko‘ring."</string>
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Siz parolni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri kiritdingiz. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> soniyadan so‘ng qayta urinib ko‘ring."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri kiritdingiz. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> soniyadan so‘ng qayta urinib ko‘ring."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Siz grafik kalitni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri kiritdingiz. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> soniyadan so‘ng qayta urinib ko‘ring."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="8774056606869646621">"Siz planshetni qulfdan chiqarish uchun <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri urinish qildingiz. Agar yana <xliff:g id="NUMBER_1">%2$d</xliff:g> marta muvaffaqiyatsiz urinish qilsangiz, ushbu planshetda zavod sozlamalari qayta tiklanadi va undagi barcha ma’lumotlar ham o‘chib ketadi."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="1843331751334128428">"Siz telefonni qulfdan chiqarish uchun <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri urinish qildingiz. Agar yana <xliff:g id="NUMBER_1">%2$d</xliff:g> marta muvaffaqiyatsiz urinish qilsangiz, ushbu telefonda zavod sozlamalari qayta tiklanadi va undagi barcha ma’lumotlar ham o‘chib ketadi."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="258925501999698032">"Siz planshetni qulfdan chiqarish uchun <xliff:g id="NUMBER">%d</xliff:g> marta noto‘g‘ri urinish qildingiz. Endi, ushbu planshetda zavod sozlamalari qayta tiklanadi va undagi barcha ma’lumotlar ham o‘chib ketadi."</string>
@@ -92,8 +87,8 @@
<string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="6853071165802933545">"Siz telefonni qulfdan chiqarish uchun <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri urinish qildingiz. Agar yana <xliff:g id="NUMBER_1">%2$d</xliff:g> marta muvaffaqiyatsiz urinish qilsangiz, ishchi profil o‘chirib tashlanadi va undagi barcha profil ma’lumotlari ham o‘chib ketadi."</string>
<string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4686386497449912146">"Siz planshetni qulfdan chiqarish uchun <xliff:g id="NUMBER">%d</xliff:g> marta noto‘g‘ri urinish qildingiz. Endi, ishchi profil o‘chirib tashlanadi va undagi barcha ma’lumotlar ham o‘chib ketadi."</string>
<string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4951507352869831265">"Siz telefonni qulfdan chiqarish uchun <xliff:g id="NUMBER">%d</xliff:g> marta noto‘g‘ri urinish qildingiz. Endi, ishchi profil o‘chirib tashlanadi va undagi barcha ma’lumotlar ham o‘chib ketadi."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri kiritdingiz. <xliff:g id="NUMBER_1">%2$d</xliff:g> marta muvaffaqiyatsiz urinishdan so‘ng, sizdan e-pochtangizdan foydalanib, planshet qulfini ochishingiz so‘raladi.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> soniyadan so‘ng yana urinib ko‘ring."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Siz chizmali kalitni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri chizdingiz. <xliff:g id="NUMBER_1">%2$d</xliff:g> marta muvaffaqiyatsiz urinishdan so‘ng, sizdan e-pochtangizdan foydalanib, telefon qulfini ochishingiz so‘raladi.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> soniyadan so‘ng yana urinib ko‘ring."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Siz grafik kalitni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri kiritdingiz. <xliff:g id="NUMBER_1">%2$d</xliff:g> marta muvaffaqiyatsiz urinishdan so‘ng, sizdan e-pochtangizdan foydalanib, planshet qulfini ochishingiz so‘raladi.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> soniyadan so‘ng yana urinib ko‘ring."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Siz grafik kalitni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri chizdingiz. <xliff:g id="NUMBER_1">%2$d</xliff:g> marta muvaffaqiyatsiz urinishdan so‘ng, sizdan e-pochtangizdan foydalanib, telefon qulfini ochishingiz so‘raladi.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> soniyadan so‘ng yana urinib ko‘ring."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="30531039455764924">"SIM karta PIN kodi noto‘g‘ri. Qurilma qulfini ochish uchun aloqa operatoringiz bilan bog‘laning."</string>
<plurals name="kg_password_wrong_pin_code" formatted="false" msgid="6721575017538162249">
<item quantity="other">SIM kartaning PIN kodi noto‘g‘ri. Sizda yana <xliff:g id="NUMBER_1">%d</xliff:g> ta urinish qoldi.</item>
@@ -110,20 +105,20 @@
<string name="keyguard_carrier_default" msgid="8700650403054042153">"Aloqa yo‘q."</string>
<string name="accessibility_ime_switch_button" msgid="2829803408288433429">"Matn kiritish usulini o‘zgartirish"</string>
<string name="airplane_mode" msgid="3122107900897202805">"Parvoz rejimi"</string>
- <string name="kg_prompt_reason_restart_pattern" msgid="5519822969283306009">"Qurilma o‘chirib yoqilgandan so‘ng chizmali kalit talab qilinadi"</string>
+ <string name="kg_prompt_reason_restart_pattern" msgid="5519822969283306009">"Qurilma o‘chirib yoqilgandan so‘ng grafik kalit talab qilinadi"</string>
<string name="kg_prompt_reason_restart_pin" msgid="4411398237158448198">"Qurilma o‘chirib yoqilgandan so‘ng PIN kod talab qilinadi"</string>
<string name="kg_prompt_reason_restart_password" msgid="6504585392626524695">"Qurilma o‘chirib yoqilgandan so‘ng parol talab qilinadi"</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="3717506169674397620">"Qo‘shimcha xavfsizlik chorasi sifatida chizmali kalit talab qilinadi"</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="3717506169674397620">"Qo‘shimcha xavfsizlik chorasi sifatida grafik kalit talab qilinadi"</string>
<string name="kg_prompt_reason_timeout_pin" msgid="6951483704195396341">"Qo‘shimcha xavfsizlik chorasi sifatida PIN kod talab qilinadi"</string>
<string name="kg_prompt_reason_timeout_password" msgid="7306667546971345027">"Qo‘shimcha xavfsizlik chorasi sifatida parol talab qilinadi"</string>
- <string name="kg_prompt_reason_switch_profiles_pattern" msgid="8476293962695171574">"Profilni amlashtirishda chizmali kalit talab qilinadi"</string>
+ <string name="kg_prompt_reason_switch_profiles_pattern" msgid="8476293962695171574">"Profilni amlashtirishda grafik kalit talab qilinadi"</string>
<string name="kg_prompt_reason_switch_profiles_pin" msgid="2343607138520460043">"Profilni amlashtirishda PIN kod talab qilinadi"</string>
<string name="kg_prompt_reason_switch_profiles_password" msgid="1295960907951965927">"Profilni amlashtirishda parol talab qilinadi"</string>
<string name="kg_prompt_reason_device_admin" msgid="5838877342219587193">"Qurilma administrator tomonidan qulflangan"</string>
<string name="kg_prompt_reason_user_request" msgid="500999297306031595">"Qurilma qo‘lda qulflangan"</string>
<plurals name="kg_prompt_reason_time_pattern" formatted="false" msgid="2697444392228541853">
- <item quantity="other">Qurilma <xliff:g id="NUMBER_1">%d</xliff:g> soatdan beri qulfdan chiqarilgani yo‘q. Chizmali kalitni yana bir marta kiriting.</item>
- <item quantity="one">Qurilma <xliff:g id="NUMBER_0">%d</xliff:g> soatdan beri qulfdan chiqarilgani yo‘q. Chizmali kalitni yana bir marta kiriting.</item>
+ <item quantity="other">Qurilma <xliff:g id="NUMBER_1">%d</xliff:g> soatdan beri qulfdan chiqarilgani yo‘q. Grafik kalitni yana bir marta chizing.</item>
+ <item quantity="one">Qurilma <xliff:g id="NUMBER_0">%d</xliff:g> soatdan beri qulfdan chiqarilgani yo‘q. Grafik kalitni yana bir marta chizing.</item>
</plurals>
<plurals name="kg_prompt_reason_time_pin" formatted="false" msgid="2118758475374354849">
<item quantity="other">Qurilma <xliff:g id="NUMBER_1">%d</xliff:g> soatdan beri qulfdan chiqarilgani yo‘q. PIN-kodni yana bir marta kiriting.</item>
diff --git a/packages/Keyguard/res/values-vi/strings.xml b/packages/Keyguard/res/values-vi/strings.xml
index ad3da9f..c6d2bd8 100644
--- a/packages/Keyguard/res/values-vi/strings.xml
+++ b/packages/Keyguard/res/values-vi/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Thẻ SIM đã bị khóa."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Thẻ SIM đã bị khóa PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Đang mở khóa thẻ SIM…"</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Mở khóa bằng hình."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Mở khóa bằng mã pin."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Mở khóa bằng mật khẩu."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Khu vực hình."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Khu vực trượt."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Khu vực mã PIN"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Khu vực mã PIN của SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Khu vực PUK của SIM"</string>
diff --git a/packages/Keyguard/res/values-zh-rCN/strings.xml b/packages/Keyguard/res/values-zh-rCN/strings.xml
index 274d9e6..0723ab1 100644
--- a/packages/Keyguard/res/values-zh-rCN/strings.xml
+++ b/packages/Keyguard/res/values-zh-rCN/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM卡已被锁定。"</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM卡已被PUK码锁定。"</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"正在解锁SIM卡..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"图案解锁。"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"PIN码解锁。"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"密码解锁。"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"图案区域。"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"滑动区域。"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN 码区域"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM 卡 PIN 码区域"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM 卡 PUK 码区域"</string>
diff --git a/packages/Keyguard/res/values-zh-rHK/strings.xml b/packages/Keyguard/res/values-zh-rHK/strings.xml
index 7d51154..5b1903b 100644
--- a/packages/Keyguard/res/values-zh-rHK/strings.xml
+++ b/packages/Keyguard/res/values-zh-rHK/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM 卡處於鎖定狀態。"</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM 卡處於 PUK 鎖定狀態。"</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"正在解開上鎖的 SIM 卡..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"圖案解鎖。"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"PIN 解鎖。"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"密碼解鎖。"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"圖案區域。"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"滑動區域。"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN 區域"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM PIN 區域"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM PUK 區域"</string>
diff --git a/packages/Keyguard/res/values-zh-rTW/strings.xml b/packages/Keyguard/res/values-zh-rTW/strings.xml
index 50895f3..388f8e1 100644
--- a/packages/Keyguard/res/values-zh-rTW/strings.xml
+++ b/packages/Keyguard/res/values-zh-rTW/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM 卡處於鎖定狀態。"</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM 卡處於 PUK 鎖定狀態"</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"正在解除 SIM 卡鎖定..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"圖案解鎖。"</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"PIN 解鎖。"</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"密碼解鎖。"</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"圖案區域。"</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"滑動區域。"</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"PIN 區"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"SIM 卡 PIN 區"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"SIM 卡 PUK 區"</string>
diff --git a/packages/Keyguard/res/values-zu/strings.xml b/packages/Keyguard/res/values-zu/strings.xml
index c5f2a85..a9b6263 100644
--- a/packages/Keyguard/res/values-zu/strings.xml
+++ b/packages/Keyguard/res/values-zu/strings.xml
@@ -46,11 +46,6 @@
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Ikhadi le-SIM likhiyiwe."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Ikhadi le-SIM likhiywe nge-PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Ivula ikhadi le-SIM..."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Ukuvula ngephethini."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Ukuvula ngephinikhodi."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Ukuvula ngephasiwedi."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Indawo yephethini."</string>
- <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Indawo yokushelelisa."</string>
<string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Indawo yephinikhodi"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Indawo yephinikhodi ye-SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Indawo ye-SIM PUK"</string>
diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml
index 09fec81..ff689aa 100644
--- a/packages/Keyguard/res/values/strings.xml
+++ b/packages/Keyguard/res/values/strings.xml
@@ -103,15 +103,6 @@
<!-- Time format strings for fall-back clock widget -->
<string name="keyguard_widget_24_hours_format" translatable="false">kk\uee01mm</string>
- <string name="keyguard_accessibility_pattern_unlock">Pattern unlock.</string>
- <!-- Accessibility description of the pin lock. [CHAR_LIMIT=none] -->
- <string name="keyguard_accessibility_pin_unlock">Pin unlock.</string>
- <!-- Accessibility description of the password lock. [CHAR_LIMIT=none] -->
- <string name="keyguard_accessibility_password_unlock">Password unlock.</string>
- <!-- Accessibility description of the unlock pattern area. [CHAR_LIMIT=none] -->
- <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">Pattern area.</string>
- <!-- Accessibility description of the unlock slide area. [CHAR_LIMIT=none] -->
- <string name="keyguard_accessibility_slide_area">Slide area.</string>
<!-- Accessibility description of the PIN password view. [CHAR_LIMIT=none] -->
<string name="keyguard_accessibility_pin_area">PIN area</string>
<!-- Accessibility description of the SIM PIN password view. [CHAR_LIMIT=none] -->
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index 60eaad2..038e08d 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -137,12 +137,21 @@
entry,
userId,
new LockPatternChecker.OnCheckCallback() {
+
+ @Override
+ public void onEarlyMatched() {
+ onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */,
+ true /* isValidPassword */);
+ }
+
@Override
public void onChecked(boolean matched, int timeoutMs) {
setPasswordEntryInputEnabled(true);
mPendingLockCheck = null;
- onPasswordChecked(userId, matched, timeoutMs,
- true /* isValidPassword */);
+ if (!matched) {
+ onPasswordChecked(userId, false /* matched */, timeoutMs,
+ true /* isValidPassword */);
+ }
}
});
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
index 7ea767c..4f5152a 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
@@ -36,6 +36,7 @@
private final AppearAnimationUtils mAppearAnimationUtils;
private final DisappearAnimationUtils mDisappearAnimationUtils;
+ private final DisappearAnimationUtils mDisappearAnimationUtilsLocked;
private ViewGroup mContainer;
private ViewGroup mRow0;
private ViewGroup mRow1;
@@ -44,6 +45,7 @@
private View mDivider;
private int mDisappearYTranslation;
private View[][] mViews;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
public KeyguardPINView(Context context) {
this(context, null);
@@ -56,8 +58,14 @@
125, 0.6f /* translationScale */,
0.45f /* delayScale */, AnimationUtils.loadInterpolator(
mContext, android.R.interpolator.fast_out_linear_in));
+ mDisappearAnimationUtilsLocked = new DisappearAnimationUtils(context,
+ (long) (125 * KeyguardPatternView.DISAPPEAR_MULTIPLIER_LOCKED),
+ 0.6f /* translationScale */,
+ 0.45f /* delayScale */, AnimationUtils.loadInterpolator(
+ mContext, android.R.interpolator.fast_out_linear_in));
mDisappearYTranslation = getResources().getDimensionPixelSize(
R.dimen.disappear_y_translation);
+ mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
}
@Override
@@ -136,7 +144,10 @@
setTranslationY(0);
AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 280 /* duration */,
mDisappearYTranslation, mDisappearAnimationUtils.getInterpolator());
- mDisappearAnimationUtils.startAnimation2d(mViews,
+ DisappearAnimationUtils disappearAnimationUtils = mKeyguardUpdateMonitor.isUserUnlocked()
+ ? mDisappearAnimationUtils
+ : mDisappearAnimationUtilsLocked;
+ disappearAnimationUtils.startAnimation2d(mViews,
new Runnable() {
@Override
public void run() {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
index b75f529..ddccc14 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
@@ -42,16 +42,21 @@
* Displays an alphanumeric (latin-1) key entry for the user to enter
* an unlock password
*/
-
public class KeyguardPasswordView extends KeyguardAbsKeyInputView
implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
private final boolean mShowImeAtScreenOn;
private final int mDisappearYTranslation;
+ // A delay constant to be used in a workaround for the situation where InputMethodManagerService
+ // is not switched to the new user yet.
+ // TODO: Remove this by ensuring such a race condition never happens.
+ private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms
+
InputMethodManager mImm;
private TextView mPasswordEntry;
private TextViewInputDisabler mPasswordEntryDisabler;
+ private View mSwitchImeButton;
private Interpolator mLinearOutSlowInInterpolator;
private Interpolator mFastOutLinearInInterpolator;
@@ -141,12 +146,31 @@
mPasswordEntry.requestFocus();
}
+ private void updateSwitchImeButton() {
+ // If there's more than one IME, enable the IME switcher button
+ final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE;
+ final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes(mImm, false);
+ if (wasVisible != shouldBeVisible) {
+ mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE);
+ }
+
+ // TODO: Check if we still need this hack.
+ // If no icon is visible, reset the start margin on the password field so the text is
+ // still centered.
+ if (mSwitchImeButton.getVisibility() != View.VISIBLE) {
+ android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams();
+ if (params instanceof MarginLayoutParams) {
+ final MarginLayoutParams mlp = (MarginLayoutParams) params;
+ mlp.setMarginStart(0);
+ mPasswordEntry.setLayoutParams(params);
+ }
+ }
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- boolean imeOrDeleteButtonVisible = false;
-
mImm = (InputMethodManager) getContext().getSystemService(
Context.INPUT_METHOD_SERVICE);
@@ -171,31 +195,29 @@
mPasswordEntry.requestFocus();
- // If there's more than one IME, enable the IME switcher button
- View switchImeButton = findViewById(R.id.switch_ime_button);
- if (switchImeButton != null && hasMultipleEnabledIMEsOrSubtypes(mImm, false)) {
- switchImeButton.setVisibility(View.VISIBLE);
- imeOrDeleteButtonVisible = true;
- switchImeButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mCallback.userActivity(); // Leave the screen on a bit longer
- // Do not show auxiliary subtypes in password lock screen.
- mImm.showInputMethodPicker(false /* showAuxiliarySubtypes */);
- }
- });
- }
-
- // If no icon is visible, reset the start margin on the password field so the text is
- // still centered.
- if (!imeOrDeleteButtonVisible) {
- android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams();
- if (params instanceof MarginLayoutParams) {
- final MarginLayoutParams mlp = (MarginLayoutParams) params;
- mlp.setMarginStart(0);
- mPasswordEntry.setLayoutParams(params);
+ mSwitchImeButton = findViewById(R.id.switch_ime_button);
+ mSwitchImeButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCallback.userActivity(); // Leave the screen on a bit longer
+ // Do not show auxiliary subtypes in password lock screen.
+ mImm.showInputMethodPicker(false /* showAuxiliarySubtypes */);
}
- }
+ });
+
+ // If there's more than one IME, enable the IME switcher button
+ updateSwitchImeButton();
+
+ // When we the current user is switching, InputMethodManagerService sometimes has not
+ // switched internal state yet here. As a quick workaround, we check the keyboard state
+ // again.
+ // TODO: Remove this workaround by ensuring such a race condition never happens.
+ postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ updateSwitchImeButton();
+ }
+ }, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON);
}
@Override
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index e070492..84b90c4 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -55,9 +55,13 @@
// how many cells the user has to cross before we poke the wakelock
private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
+ // How much we scale up the duration of the disappear animation when the current user is locked
+ public static final float DISAPPEAR_MULTIPLIER_LOCKED = 1.5f;
+
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final AppearAnimationUtils mAppearAnimationUtils;
private final DisappearAnimationUtils mDisappearAnimationUtils;
+ private final DisappearAnimationUtils mDisappearAnimationUtilsLocked;
private CountDownTimer mCountdownTimer = null;
private LockPatternUtils mLockPatternUtils;
@@ -109,6 +113,10 @@
125, 1.2f /* translationScale */,
0.6f /* delayScale */, AnimationUtils.loadInterpolator(
mContext, android.R.interpolator.fast_out_linear_in));
+ mDisappearAnimationUtilsLocked = new DisappearAnimationUtils(context,
+ (long) (125 * DISAPPEAR_MULTIPLIER_LOCKED), 1.2f /* translationScale */,
+ 0.6f /* delayScale */, AnimationUtils.loadInterpolator(
+ mContext, android.R.interpolator.fast_out_linear_in));
mDisappearYTranslation = getResources().getDimensionPixelSize(
R.dimen.disappear_y_translation);
}
@@ -239,11 +247,21 @@
pattern,
userId,
new LockPatternChecker.OnCheckCallback() {
+
+ @Override
+ public void onEarlyMatched() {
+ onPatternChecked(userId, true /* matched */, 0 /* timeoutMs */,
+ true /* isValidPattern */);
+ }
+
@Override
public void onChecked(boolean matched, int timeoutMs) {
mLockPatternView.enableInput();
mPendingLockCheck = null;
- onPatternChecked(userId, matched, timeoutMs, true);
+ if (!matched) {
+ onPatternChecked(userId, false /* matched */, timeoutMs,
+ true /* isValidPattern */);
+ }
}
});
if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
@@ -390,25 +408,30 @@
@Override
public boolean startDisappearAnimation(final Runnable finishRunnable) {
+ float durationMultiplier = mKeyguardUpdateMonitor.isUserUnlocked()
+ ? 1f
+ : DISAPPEAR_MULTIPLIER_LOCKED;
mLockPatternView.clearPattern();
enableClipping(false);
setTranslationY(0);
- AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 300 /* duration */,
+ AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */,
+ (long) (300 * durationMultiplier),
-mDisappearAnimationUtils.getStartTranslation(),
mDisappearAnimationUtils.getInterpolator());
- mDisappearAnimationUtils.startAnimation2d(mLockPatternView.getCellStates(),
- new Runnable() {
- @Override
- public void run() {
- enableClipping(true);
- if (finishRunnable != null) {
- finishRunnable.run();
- }
+
+ DisappearAnimationUtils disappearAnimationUtils = mKeyguardUpdateMonitor.isUserUnlocked()
+ ? mDisappearAnimationUtils
+ : mDisappearAnimationUtilsLocked;
+ disappearAnimationUtils.startAnimation2d(mLockPatternView.getCellStates(),
+ () -> {
+ enableClipping(true);
+ if (finishRunnable != null) {
+ finishRunnable.run();
}
}, KeyguardPatternView.this);
if (!TextUtils.isEmpty(mSecurityMessageDisplay.getText())) {
mDisappearAnimationUtils.createAnimation(mSecurityMessageDisplay, 0,
- 200,
+ (long) (200 * durationMultiplier),
- mDisappearAnimationUtils.getStartTranslation() * 3,
false /* appearing */,
mDisappearAnimationUtils.getInterpolator(),
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 8d6e07e..108b466 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -20,13 +20,14 @@
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.View;
/**
* A Pin based Keyguard input view
*/
public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
- implements View.OnKeyListener {
+ implements View.OnKeyListener, View.OnTouchListener {
protected PasswordTextView mPasswordEntry;
private View mOkButton;
@@ -185,10 +186,10 @@
mOkButton = findViewById(R.id.key_enter);
if (mOkButton != null) {
+ mOkButton.setOnTouchListener(this);
mOkButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- doHapticKeyClick();
if (mPasswordEntry.isEnabled()) {
verifyPasswordAndUnlock();
}
@@ -199,6 +200,7 @@
mDeleteButton = findViewById(R.id.delete_button);
mDeleteButton.setVisibility(View.VISIBLE);
+ mDeleteButton.setOnTouchListener(this);
mDeleteButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@@ -206,7 +208,6 @@
if (mPasswordEntry.isEnabled()) {
mPasswordEntry.deleteLastChar();
}
- doHapticKeyClick();
}
});
mDeleteButton.setOnLongClickListener(new View.OnLongClickListener() {
@@ -237,6 +238,14 @@
}
@Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ doHapticKeyClick();
+ }
+ return false;
+ }
+
+ @Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
return onKeyDown(keyCode, event);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
index aa74940..8290842 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
@@ -49,11 +49,6 @@
int PROMPT_REASON_AFTER_LOCKOUT = 5;
/**
- * Some auth is required because a single wrong credential has been tried.
- */
- int PROMPT_REASON_WRONG_CREDENTIAL = 6;
-
- /**
* Interface back to keyguard to tell it when security
* @param callback
*/
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
index 9d1df26..e1657c7 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -127,6 +127,11 @@
super.onConfigurationChanged(newConfig);
mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
getResources().getDimensionPixelSize(R.dimen.widget_big_font_size));
+ // Some layouts like burmese have a different margin for the clock
+ MarginLayoutParams layoutParams = (MarginLayoutParams) mClockView.getLayoutParams();
+ layoutParams.bottomMargin = getResources().getDimensionPixelSize(
+ R.dimen.bottom_text_spacing_digital);
+ mClockView.setLayoutParams(layoutParams);
mDateView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
getResources().getDimensionPixelSize(R.dimen.widget_label_font_size));
mOwnerInfo.setTextSize(TypedValue.COMPLEX_UNIT_PX,
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index a8419bf..56f3741 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -16,6 +16,16 @@
package com.android.keyguard;
+import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN;
+import static android.os.BatteryManager.BATTERY_STATUS_FULL;
+import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
+import static android.os.BatteryManager.EXTRA_HEALTH;
+import static android.os.BatteryManager.EXTRA_LEVEL;
+import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT;
+import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE;
+import static android.os.BatteryManager.EXTRA_PLUGGED;
+import static android.os.BatteryManager.EXTRA_STATUS;
+
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AlarmManager;
@@ -29,7 +39,6 @@
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.graphics.Bitmap;
-import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
@@ -41,7 +50,9 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
@@ -69,16 +80,6 @@
import java.util.List;
import java.util.Map.Entry;
-import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN;
-import static android.os.BatteryManager.BATTERY_STATUS_FULL;
-import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
-import static android.os.BatteryManager.EXTRA_HEALTH;
-import static android.os.BatteryManager.EXTRA_LEVEL;
-import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT;
-import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE;
-import static android.os.BatteryManager.EXTRA_PLUGGED;
-import static android.os.BatteryManager.EXTRA_STATUS;
-
/**
* Watches for updates that may be interesting to the keyguard, and provides
* the up to date information as well as a registration for callbacks that care
@@ -176,6 +177,8 @@
private boolean mGoingToSleep;
private boolean mBouncer;
private boolean mBootCompleted;
+ private boolean mUserUnlocked;
+ private boolean mHasLockscreenWallpaper;
// Device provisioning state
private boolean mDeviceProvisioned;
@@ -202,6 +205,7 @@
private AlarmManager mAlarmManager;
private List<SubscriptionInfo> mSubscriptionInfo;
private TrustManager mTrustManager;
+ private UserManager mUserManager;
private int mFingerprintRunningState = FINGERPRINT_STATE_STOPPED;
private final Handler mHandler = new Handler() {
@@ -257,10 +261,14 @@
handleFinishedGoingToSleep(msg.arg1);
break;
case MSG_STARTED_WAKING_UP:
+ Trace.beginSection("KeyguardUpdateMonitor#handler MSG_STARTED_WAKING_UP");
handleStartedWakingUp();
+ Trace.endSection();
break;
case MSG_FACE_UNLOCK_STATE_CHANGED:
+ Trace.beginSection("KeyguardUpdateMonitor#handler MSG_FACE_UNLOCK_STATE_CHANGED");
handleFaceUnlockStateChanged(msg.arg1 != 0, msg.arg2);
+ Trace.endSection();
break;
case MSG_SIM_SUBSCRIPTION_INFO_CHANGED:
handleSimSubscriptionInfoChanged();
@@ -275,7 +283,9 @@
handleScreenTurnedOn();
break;
case MSG_SCREEN_TURNED_OFF:
+ Trace.beginSection("KeyguardUpdateMonitor#handler MSG_SCREEN_TURNED_ON");
handleScreenTurnedOff();
+ Trace.endSection();
break;
}
}
@@ -397,6 +407,7 @@
}
private void onFingerprintAuthenticated(int userId) {
+ Trace.beginSection("KeyGuardUpdateMonitor#onFingerPrintAuthenticated");
mUserFingerprintAuthenticated.put(userId, true);
// If fingerprint unlocking is allowed, this event will lead to a Keyguard dismiss or to a
@@ -409,6 +420,7 @@
cb.onFingerprintAuthenticated(userId);
}
}
+ Trace.endSection();
}
private void handleFingerprintAuthFailed() {
@@ -433,7 +445,8 @@
}
}
- private void handleFingerprintAuthenticated() {
+ private void handleFingerprintAuthenticated(int authUserId) {
+ Trace.beginSection("KeyGuardUpdateMonitor#handlerFingerPrintAuthenticated");
try {
final int userId;
try {
@@ -442,6 +455,10 @@
Log.e(TAG, "Failed to get current user id: ", e);
return;
}
+ if (userId != authUserId) {
+ Log.d(TAG, "Fingerprint authenticated for wrong user: " + authUserId);
+ return;
+ }
if (isFingerprintDisabled(userId)) {
Log.d(TAG, "Fingerprint disabled by DPM for userId: " + userId);
return;
@@ -450,6 +467,7 @@
} finally {
setFingerprintRunningState(FINGERPRINT_STATE_STOPPED);
}
+ Trace.endSection();
}
private void handleFingerprintHelp(int msgId, String helpString) {
@@ -554,6 +572,10 @@
&& !hasFingerprintUnlockTimedOut(sCurrentUser);
}
+ public boolean isUserUnlocked() {
+ return mUserUnlocked;
+ }
+
public StrongAuthTracker getStrongAuthTracker() {
return mStrongAuthTracker;
}
@@ -684,8 +706,10 @@
mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_INFO_CHANGED,
intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId()), 0));
} else if (ACTION_FACE_UNLOCK_STARTED.equals(action)) {
+ Trace.beginSection("KeyguardUpdateMonitor.mBroadcastAllReceiver#onReceive ACTION_FACE_UNLOCK_STARTED");
mHandler.sendMessage(mHandler.obtainMessage(MSG_FACE_UNLOCK_STATE_CHANGED, 1,
getSendingUserId()));
+ Trace.endSection();
} else if (ACTION_FACE_UNLOCK_STOPPED.equals(action)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_FACE_UNLOCK_STATE_CHANGED, 0,
getSendingUserId()));
@@ -725,7 +749,9 @@
@Override
public void onAuthenticationSucceeded(AuthenticationResult result) {
- handleFingerprintAuthenticated();
+ Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded");
+ handleFingerprintAuthenticated(result.getUserId());
+ Trace.endSection();
}
@Override
@@ -900,6 +926,7 @@
}
protected void handleStartedWakingUp() {
+ Trace.beginSection("KeyguardUpdateMonitor#handleStartedWakingUp");
updateFingerprintListeningState();
final int count = mCallbacks.size();
for (int i = 0; i < count; i++) {
@@ -908,6 +935,7 @@
cb.onStartedWakingUp();
}
}
+ Trace.endSection();
}
protected void handleStartedGoingToSleep(int arg1) {
@@ -1040,10 +1068,9 @@
public void onForegroundProfileSwitch(int newProfileId) {
// Ignore.
}
- });
+ }, TAG);
} catch (RemoteException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ e.rethrowAsRuntimeException();
}
IntentFilter strongAuthTimeoutFilter = new IntentFilter();
@@ -1059,6 +1086,8 @@
if (mFpm != null) {
mFpm.addLockoutResetCallback(mLockoutResetCallback);
}
+
+ mUserManager = context.getSystemService(UserManager.class);
}
private void updateFingerprintListeningState() {
@@ -1145,6 +1174,30 @@
}
/**
+ * Update the state whether Keyguard currently has a lockscreen wallpaper.
+ *
+ * @param hasLockscreenWallpaper Whether Keyguard has a lockscreen wallpaper.
+ */
+ public void setHasLockscreenWallpaper(boolean hasLockscreenWallpaper) {
+ if (hasLockscreenWallpaper != mHasLockscreenWallpaper) {
+ mHasLockscreenWallpaper = hasLockscreenWallpaper;
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onHasLockscreenWallpaperChanged(hasLockscreenWallpaper);
+ }
+ }
+ }
+ }
+
+ /**
+ * @return Whether Keyguard has a lockscreen wallpaper.
+ */
+ public boolean hasLockscreenWallpaper() {
+ return mHasLockscreenWallpaper;
+ }
+
+ /**
* Handle {@link #MSG_DPM_STATE_CHANGED}
*/
protected void handleDevicePolicyManagerStateChanged() {
@@ -1391,6 +1444,7 @@
private void handleKeyguardReset() {
if (DEBUG) Log.d(TAG, "handleKeyguardReset");
updateFingerprintListeningState();
+ mUserUnlocked = mUserManager.isUserUnlocked(getCurrentUser());
}
/**
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index bd6c51c..4a2d356 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -240,4 +240,9 @@
* has changed.
*/
public void onStrongAuthStateChanged(int userId) { }
+
+ /**
+ * Called when the state whether we have a lockscreen wallpaper has changed.
+ */
+ public void onHasLockscreenWallpaperChanged(boolean hasLockscreenWallpaper) { }
}
diff --git a/packages/Keyguard/src/com/android/keyguard/NumPadKey.java b/packages/Keyguard/src/com/android/keyguard/NumPadKey.java
index 2ff7e12..1518bdc 100644
--- a/packages/Keyguard/src/com/android/keyguard/NumPadKey.java
+++ b/packages/Keyguard/src/com/android/keyguard/NumPadKey.java
@@ -23,6 +23,7 @@
import android.util.AttributeSet;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
@@ -53,8 +54,7 @@
if (mTextView != null && mTextView.isEnabled()) {
mTextView.append(Character.forDigit(mDigit, 10));
}
- userActivity();
- doHapticKeyClick();
+ userActivity();;
}
};
@@ -126,6 +126,14 @@
}
@Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ doHapticKeyClick();
+ }
+ return super.onTouchEvent(event);
+ }
+
+ @Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
diff --git a/packages/PrintSpooler/res/drawable/print_button.xml b/packages/PrintSpooler/res/drawable/print_button.xml
index b59afba..0114103 100644
--- a/packages/PrintSpooler/res/drawable/print_button.xml
+++ b/packages/PrintSpooler/res/drawable/print_button.xml
@@ -16,7 +16,7 @@
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/print_button_tint_color">
+ android:color="?android:attr/colorControlHighlight">
<item
android:drawable="@drawable/print_button_background">
</item>
diff --git a/packages/PrintSpooler/res/values-my-rMM/strings.xml b/packages/PrintSpooler/res/values-my-rMM/strings.xml
index c277c55..11b1664 100644
--- a/packages/PrintSpooler/res/values-my-rMM/strings.xml
+++ b/packages/PrintSpooler/res/values-my-rMM/strings.xml
@@ -33,7 +33,7 @@
<string name="pages_range_example" msgid="8558694453556945172">"ဥပမာ ၁-၅၊ ၈၊ ၁၁-၁၃"</string>
<string name="print_preview" msgid="8010217796057763343">"အစမ်းကြည့်ရှုရန်"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"အစမ်းကြည့်ရန် ပီဒီအက်ဖ် ဖတ်ရှုစရာ ထည့်သွင်းပါ"</string>
- <string name="printing_app_crashed" msgid="854477616686566398">"စာထုတ်လုပ်သော အက်ပ် ခဏ ပျက်သွားပါသည်"</string>
+ <string name="printing_app_crashed" msgid="854477616686566398">"စာထုတ်လုပ်သော အက်ပ်ခဏ ပျက်သွားပါသည်"</string>
<string name="generating_print_job" msgid="3119608742651698916">"စာထုတ်အလုပ်ကို လုပ်နေပါသည်"</string>
<string name="save_as_pdf" msgid="5718454119847596853">"ပီဒီအက်ဖ် အဖြစ်သိမ်းဆည်းရန်"</string>
<string name="all_printers" msgid="5018829726861876202">"စာထုတ်စက် အားလုံး"</string>
@@ -81,7 +81,7 @@
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ကို ပယ်ဖျက်နေပါသည်"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"စာထုတ်စက်မှ အမှား <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="blocked_notification_title_template" msgid="1175435827331588646">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ကိုစာထုတ်စက်ကငြင်းလိုက်သည်"</string>
- <string name="cancel" msgid="4373674107267141885">"မလုပ်တော့ပါ"</string>
+ <string name="cancel" msgid="4373674107267141885">"မလုပ်တော့"</string>
<string name="restart" msgid="2472034227037808749">"အစက ပြန်စရန်"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"စာထုတ်စက်နဲ့ ဆက်သွယ်ထားမှု မရှိပါ"</string>
<string name="reason_unknown" msgid="5507940196503246139">"အကြောင်းအရာ မသိရှိ"</string>
diff --git a/packages/PrintSpooler/res/values/colors.xml b/packages/PrintSpooler/res/values/colors.xml
index 47e616e..9464c67 100644
--- a/packages/PrintSpooler/res/values/colors.xml
+++ b/packages/PrintSpooler/res/values/colors.xml
@@ -16,8 +16,6 @@
<resources>
- <color name="print_button_tint_color">#EEFF41</color>
-
<color name="print_preview_scrim_color">#99000000</color>
<color name="print_preview_background_color">#F2F1F2</color>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
index 999d82d..6140428 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
@@ -838,9 +838,15 @@
try (ParcelFileDescriptor source = pipe[0]) {
try (ParcelFileDescriptor destination = pipe[1]) {
-
- mRenderer.renderPage(mPageIndex, bitmap.getWidth(), bitmap.getHeight(),
- mRenderSpec.printAttributes, destination);
+ synchronized (mLock) {
+ if (mRenderer != null) {
+ mRenderer.renderPage(mPageIndex, bitmap.getWidth(),
+ bitmap.getHeight(), mRenderSpec.printAttributes,
+ destination);
+ } else {
+ throw new IllegalStateException("Renderer is disconnected");
+ }
+ }
}
BitmapSerializeUtils.readBitmapPixels(bitmap, source);
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
index 99145b7b..eea92b5 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
@@ -94,6 +94,7 @@
// but the content has changed.
if (mNextCommand == null) {
if (mUpdateSpec.pages != null && (mDocumentInfo.changed
+ || mDocumentInfo.writtenPages == null
|| (mDocumentInfo.info.getPageCount()
!= PrintDocumentInfo.PAGE_COUNT_UNKNOWN
&& !PageRangeUtils.contains(mDocumentInfo.writtenPages,
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java
index 2f58de5..c06e849 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java
@@ -737,9 +737,11 @@
public void updateInstalledServices(List<PrintServiceInfo> services) {
mInstalledServices.clear();
- final int numServices = services.size();
- for (int i = 0; i < numServices; i++) {
- mInstalledServices.add(services.get(i).getComponentName().getPackageName());
+ if (services != null) {
+ final int numServices = services.size();
+ for (int i = 0; i < numServices; i++) {
+ mInstalledServices.add(services.get(i).getComponentName().getPackageName());
+ }
}
filterRecommendations();
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index c318275..3b25edb 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -244,6 +244,8 @@
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setTitle(R.string.print_dialog);
+
Bundle extras = getIntent().getExtras();
mPrintJob = extras.getParcelable(PrintManager.EXTRA_PRINT_JOB);
@@ -298,7 +300,6 @@
// Now that we are bound to the local print spooler service
// and the printer registry loaded the historical printers
// we can show the UI without flickering.
- setTitle(R.string.print_dialog);
setContentView(R.layout.print_activity);
try {
@@ -526,6 +527,14 @@
.setContentType(info.getContentType())
.setPageCount(pageCount)
.build();
+
+ File file = mFileProvider.acquireFile(null);
+ try {
+ adjustedInfo.setDataSize(file.length());
+ } finally {
+ mFileProvider.releaseFile();
+ }
+
mPrintJob.setDocumentInfo(adjustedInfo);
mPrintJob.setPages(document.printedPages);
}
@@ -2060,6 +2069,7 @@
if (mPrinterRegistry != null) {
mPrinterRegistry.setTrackedPrinter(null);
+ mPrinterRegistry.setOnPrintersChangeListener(null);
}
if (mPrintersObserver != null) {
@@ -3076,6 +3086,14 @@
.setContentType(oldDocInfo.getContentType())
.setPageCount(newPageCount)
.build();
+
+ File file = mFileProvider.acquireFile(null);
+ try {
+ newDocInfo.setDataSize(file.length());
+ } finally {
+ mFileProvider.releaseFile();
+ }
+
mPrintJob.setDocumentInfo(newDocInfo);
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java
index 86366dd..9fca959 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java
@@ -120,13 +120,11 @@
@Override
public void onLoaderReset(Loader<List<PrinterInfo>> loader) {
mPrinters.clear();
- if (mOnPrintersChangeListener != null) {
- // Post a message as we are in onLoadFinished and certain operations
- // are not allowed in this callback, such as fragment transactions.
- // Clients should not handle this explicitly.
- mHandler.obtainMessage(MyHandler.MSG_PRINTERS_INVALID,
- mOnPrintersChangeListener).sendToTarget();
- }
+
+ // Post a message as we are in onLoadFinished and certain operations
+ // are not allowed in this callback, such as fragment transactions.
+ // Clients should not handle this explicitly.
+ mHandler.obtainMessage(MyHandler.MSG_PRINTERS_INVALID).sendToTarget();
}
// LoaderCallbacks#onLoadFinished
@@ -134,15 +132,12 @@
public void onLoadFinished(Loader<List<PrinterInfo>> loader, List<PrinterInfo> printers) {
mPrinters.clear();
mPrinters.addAll(printers);
- if (mOnPrintersChangeListener != null) {
- // Post a message as we are in onLoadFinished and certain operations
- // are not allowed in this callback, such as fragment transactions.
- // Clients should not handle this explicitly.
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = mOnPrintersChangeListener;
- args.arg2 = printers;
- mHandler.obtainMessage(MyHandler.MSG_PRINTERS_CHANGED, args).sendToTarget();
- }
+
+ // Post a message as we are in onLoadFinished and certain operations
+ // are not allowed in this callback, such as fragment transactions.
+ // Clients should not handle this explicitly.
+ mHandler.obtainMessage(MyHandler.MSG_PRINTERS_CHANGED, printers).sendToTarget();
+
if (!mReady) {
mReady = true;
if (mReadyCallback != null) {
@@ -158,7 +153,7 @@
}
};
- private static final class MyHandler extends Handler {
+ private final class MyHandler extends Handler {
public static final int MSG_PRINTERS_CHANGED = 0;
public static final int MSG_PRINTERS_INVALID = 1;
@@ -171,16 +166,17 @@
public void handleMessage(Message message) {
switch (message.what) {
case MSG_PRINTERS_CHANGED: {
- SomeArgs args = (SomeArgs) message.obj;
- OnPrintersChangeListener callback = (OnPrintersChangeListener) args.arg1;
- List<PrinterInfo> printers = (List<PrinterInfo>) args.arg2;
- args.recycle();
- callback.onPrintersChanged(printers);
+ List<PrinterInfo> printers = (List<PrinterInfo>) message.obj;
+
+ if (mOnPrintersChangeListener != null) {
+ mOnPrintersChangeListener.onPrintersChanged(printers);
+ }
} break;
case MSG_PRINTERS_INVALID: {
- OnPrintersChangeListener callback = (OnPrintersChangeListener) message.obj;
- callback.onPrintersInvalid();
+ if (mOnPrintersChangeListener != null) {
+ mOnPrintersChangeListener.onPrintersInvalid();
+ }
} break;
}
}
diff --git a/packages/SettingsLib/res/drawable-hdpi/ic_bt_cellphone.png b/packages/SettingsLib/res/drawable-hdpi/ic_bt_cellphone.png
deleted file mode 100644
index 6e29d23..0000000
--- a/packages/SettingsLib/res/drawable-hdpi/ic_bt_cellphone.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-hdpi/ic_bt_headphones_a2dp.png b/packages/SettingsLib/res/drawable-hdpi/ic_bt_headphones_a2dp.png
deleted file mode 100644
index 6110e9e..0000000
--- a/packages/SettingsLib/res/drawable-hdpi/ic_bt_headphones_a2dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-hdpi/ic_bt_headset_hfp.png b/packages/SettingsLib/res/drawable-hdpi/ic_bt_headset_hfp.png
deleted file mode 100644
index 6cca225..0000000
--- a/packages/SettingsLib/res/drawable-hdpi/ic_bt_headset_hfp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-hdpi/ic_bt_misc_hid.png b/packages/SettingsLib/res/drawable-hdpi/ic_bt_misc_hid.png
deleted file mode 100644
index 6445f2a..0000000
--- a/packages/SettingsLib/res/drawable-hdpi/ic_bt_misc_hid.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-hdpi/ic_bt_network_pan.png b/packages/SettingsLib/res/drawable-hdpi/ic_bt_network_pan.png
deleted file mode 100644
index 78c0294..0000000
--- a/packages/SettingsLib/res/drawable-hdpi/ic_bt_network_pan.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-hdpi/ic_bt_pointing_hid.png b/packages/SettingsLib/res/drawable-hdpi/ic_bt_pointing_hid.png
deleted file mode 100644
index 2fcc3b0..0000000
--- a/packages/SettingsLib/res/drawable-hdpi/ic_bt_pointing_hid.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-hdpi/ic_lockscreen_ime.png b/packages/SettingsLib/res/drawable-hdpi/ic_lockscreen_ime.png
deleted file mode 100644
index 70d35bf..0000000
--- a/packages/SettingsLib/res/drawable-hdpi/ic_lockscreen_ime.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-ldrtl-hdpi/ic_bt_cellphone.png b/packages/SettingsLib/res/drawable-ldrtl-hdpi/ic_bt_cellphone.png
deleted file mode 100644
index 2d9b75e..0000000
--- a/packages/SettingsLib/res/drawable-ldrtl-hdpi/ic_bt_cellphone.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-ldrtl-mdpi/ic_bt_cellphone.png b/packages/SettingsLib/res/drawable-ldrtl-mdpi/ic_bt_cellphone.png
deleted file mode 100644
index b6ebe34..0000000
--- a/packages/SettingsLib/res/drawable-ldrtl-mdpi/ic_bt_cellphone.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-ldrtl-xhdpi/ic_bt_cellphone.png b/packages/SettingsLib/res/drawable-ldrtl-xhdpi/ic_bt_cellphone.png
deleted file mode 100644
index 8b67b91..0000000
--- a/packages/SettingsLib/res/drawable-ldrtl-xhdpi/ic_bt_cellphone.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-mdpi/ic_bt_cellphone.png b/packages/SettingsLib/res/drawable-mdpi/ic_bt_cellphone.png
deleted file mode 100644
index 1fa0a3d..0000000
--- a/packages/SettingsLib/res/drawable-mdpi/ic_bt_cellphone.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-mdpi/ic_bt_headphones_a2dp.png b/packages/SettingsLib/res/drawable-mdpi/ic_bt_headphones_a2dp.png
deleted file mode 100644
index 175bd78..0000000
--- a/packages/SettingsLib/res/drawable-mdpi/ic_bt_headphones_a2dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-mdpi/ic_bt_headset_hfp.png b/packages/SettingsLib/res/drawable-mdpi/ic_bt_headset_hfp.png
deleted file mode 100644
index 05b27e8..0000000
--- a/packages/SettingsLib/res/drawable-mdpi/ic_bt_headset_hfp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-mdpi/ic_bt_misc_hid.png b/packages/SettingsLib/res/drawable-mdpi/ic_bt_misc_hid.png
deleted file mode 100644
index 6e9f8ae..0000000
--- a/packages/SettingsLib/res/drawable-mdpi/ic_bt_misc_hid.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-mdpi/ic_bt_network_pan.png b/packages/SettingsLib/res/drawable-mdpi/ic_bt_network_pan.png
deleted file mode 100644
index 5f3371f..0000000
--- a/packages/SettingsLib/res/drawable-mdpi/ic_bt_network_pan.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-mdpi/ic_bt_pointing_hid.png b/packages/SettingsLib/res/drawable-mdpi/ic_bt_pointing_hid.png
deleted file mode 100644
index 539d77f..0000000
--- a/packages/SettingsLib/res/drawable-mdpi/ic_bt_pointing_hid.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-mdpi/ic_lockscreen_ime.png b/packages/SettingsLib/res/drawable-mdpi/ic_lockscreen_ime.png
deleted file mode 100644
index 3216776..0000000
--- a/packages/SettingsLib/res/drawable-mdpi/ic_lockscreen_ime.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xhdpi/ic_bt_cellphone.png b/packages/SettingsLib/res/drawable-xhdpi/ic_bt_cellphone.png
deleted file mode 100644
index 4f381ba..0000000
--- a/packages/SettingsLib/res/drawable-xhdpi/ic_bt_cellphone.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xhdpi/ic_bt_headphones_a2dp.png b/packages/SettingsLib/res/drawable-xhdpi/ic_bt_headphones_a2dp.png
deleted file mode 100644
index c67127d..0000000
--- a/packages/SettingsLib/res/drawable-xhdpi/ic_bt_headphones_a2dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xhdpi/ic_bt_headset_hfp.png b/packages/SettingsLib/res/drawable-xhdpi/ic_bt_headset_hfp.png
deleted file mode 100644
index d3b356b..0000000
--- a/packages/SettingsLib/res/drawable-xhdpi/ic_bt_headset_hfp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xhdpi/ic_bt_misc_hid.png b/packages/SettingsLib/res/drawable-xhdpi/ic_bt_misc_hid.png
deleted file mode 100644
index 2d38129..0000000
--- a/packages/SettingsLib/res/drawable-xhdpi/ic_bt_misc_hid.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xhdpi/ic_bt_network_pan.png b/packages/SettingsLib/res/drawable-xhdpi/ic_bt_network_pan.png
deleted file mode 100644
index fb76575..0000000
--- a/packages/SettingsLib/res/drawable-xhdpi/ic_bt_network_pan.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xhdpi/ic_bt_pointing_hid.png b/packages/SettingsLib/res/drawable-xhdpi/ic_bt_pointing_hid.png
deleted file mode 100644
index d8b68eb..0000000
--- a/packages/SettingsLib/res/drawable-xhdpi/ic_bt_pointing_hid.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xhdpi/ic_lockscreen_ime.png b/packages/SettingsLib/res/drawable-xhdpi/ic_lockscreen_ime.png
deleted file mode 100644
index 02cc3af..0000000
--- a/packages/SettingsLib/res/drawable-xhdpi/ic_lockscreen_ime.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_cellphone.png b/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_cellphone.png
deleted file mode 100644
index 7805b7a..0000000
--- a/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_cellphone.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_headphones_a2dp.png b/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_headphones_a2dp.png
deleted file mode 100644
index 8127774..0000000
--- a/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_headphones_a2dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_headset_hfp.png b/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_headset_hfp.png
deleted file mode 100644
index 84b8085..0000000
--- a/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_headset_hfp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_misc_hid.png b/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_misc_hid.png
deleted file mode 100644
index 289d6ac..0000000
--- a/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_misc_hid.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_network_pan.png b/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_network_pan.png
deleted file mode 100644
index 72bc804..0000000
--- a/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_network_pan.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_pointing_hid.png b/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_pointing_hid.png
deleted file mode 100644
index e31ce2b..0000000
--- a/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_pointing_hid.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxhdpi/ic_lockscreen_ime.png b/packages/SettingsLib/res/drawable-xxhdpi/ic_lockscreen_ime.png
deleted file mode 100644
index f23b0e7..0000000
--- a/packages/SettingsLib/res/drawable-xxhdpi/ic_lockscreen_ime.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_cellphone.png b/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_cellphone.png
deleted file mode 100644
index 1e12f96..0000000
--- a/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_cellphone.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_headphones_a2dp.png b/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_headphones_a2dp.png
deleted file mode 100644
index 8b547d9..0000000
--- a/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_headphones_a2dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_headset_hfp.png b/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_headset_hfp.png
deleted file mode 100644
index 03c5033..0000000
--- a/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_headset_hfp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_misc_hid.png b/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_misc_hid.png
deleted file mode 100644
index b9a9923..0000000
--- a/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_misc_hid.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_network_pan.png b/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_network_pan.png
deleted file mode 100644
index 989e1ab..0000000
--- a/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_network_pan.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_pointing_hid.png b/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_pointing_hid.png
deleted file mode 100644
index de8c389..0000000
--- a/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_pointing_hid.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxxhdpi/ic_lockscreen_ime.png b/packages/SettingsLib/res/drawable-xxxhdpi/ic_lockscreen_ime.png
deleted file mode 100644
index 2eb8a92..0000000
--- a/packages/SettingsLib/res/drawable-xxxhdpi/ic_lockscreen_ime.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/res/drawable/ic_bt_cellphone.xml b/packages/SettingsLib/res/drawable/ic_bt_cellphone.xml
new file mode 100644
index 0000000..cc9b732
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_bt_cellphone.xml
@@ -0,0 +1,29 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorAccent"
+ android:autoMirrored="true">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6.62,10.79c1.44,2.83 3.76,5.14 6.59,6.59l2.2,-2.2c0.27,-0.27 0.67,-0.36
+ 1.02,-0.24 1.12,0.37 2.33,0.57 3.57,0.57 0.55,0 1,0.45 1,1V20c0,0.55 -0.45,1 -1,1
+ -9.39,0 -17,-7.61 -17,-17 0,-0.55 0.45,-1 1,-1h3.5c0.55,0 1,0.45 1,1 0,1.25 0.2,2.45
+ 0.57,3.57 0.11,0.35 0.03,0.74 -0.25,1.02l-2.2,2.2z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable/ic_bt_headphones_a2dp.xml b/packages/SettingsLib/res/drawable/ic_bt_headphones_a2dp.xml
new file mode 100644
index 0000000..bbe3914
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_bt_headphones_a2dp.xml
@@ -0,0 +1,26 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorAccent">
+ <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
+ 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h3c1.66,0 3,-1.34 3,-3v-7c0,-4.97 -4.03,-9 -9,-9z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable/ic_bt_headset_hfp.xml b/packages/SettingsLib/res/drawable/ic_bt_headset_hfp.xml
new file mode 100644
index 0000000..ceeef19
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_bt_headset_hfp.xml
@@ -0,0 +1,27 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorAccent">
+ <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
+ 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h4v1h-7v2h6c1.66,0 3,-1.34 3,-3V10c0,-4.97 -4.03,-9
+ -9,-9z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable/ic_bt_misc_hid.xml b/packages/SettingsLib/res/drawable/ic_bt_misc_hid.xml
new file mode 100644
index 0000000..67b42ae
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_bt_misc_hid.xml
@@ -0,0 +1,26 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorAccent">
+ <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
+ -3,3zM16.5,9l-3,3 3,3H22V9h-5.5z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable/ic_bt_network_pan.xml b/packages/SettingsLib/res/drawable/ic_bt_network_pan.xml
new file mode 100644
index 0000000..c5ab01c
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_bt_network_pan.xml
@@ -0,0 +1,30 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorAccent">
+ <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
+ -0.16,-1.59 -0.43,-2.31l-2.33,2.32zM19.53,6.71l-1.26,1.26c0.63,1.21 0.98,2.57
+ 0.98,4.02s-0.36,2.82 -0.98,4.02l1.2,1.2c0.97,-1.54 1.54,-3.36 1.54,-5.31 -0.01,-1.89
+ -0.55,-3.67 -1.48,-5.19zM15.71,7.71L10,2L9,2v7.59L4.41,5 3,6.41 8.59,12 3,17.59 4.41,19
+ 9,14.41L9,22h1l5.71,-5.71 -4.3,-4.29
+ 4.3,-4.29zM11,5.83l1.88,1.88L11,9.59L11,5.83zM12.88,16.29L11,18.17v-3.76l1.88,1.88z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable/ic_bt_pointing_hid.xml b/packages/SettingsLib/res/drawable/ic_bt_pointing_hid.xml
new file mode 100644
index 0000000..821618d
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_bt_pointing_hid.xml
@@ -0,0 +1,26 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorAccent">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,1.07L13,9h7c0,-4.08 -3.05,-7.44 -7,-7.93zM4,15c0,4.42
+ 3.58,8 8,8s8,-3.58 8,-8v-4L4,11v4zM11,1.07C7.05,1.56 4,4.92 4,9h7L11,1.07z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable/ic_lockscreen_ime.xml b/packages/SettingsLib/res/drawable/ic_lockscreen_ime.xml
new file mode 100644
index 0000000..4aa8569
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_lockscreen_ime.xml
@@ -0,0 +1,26 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorAccent">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,5L4,5c-1.1,0 -1.99,0.9 -1.99,2L2,17c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9
+ 2,-2L22,7c0,-1.1 -0.9,-2 -2,-2zM11,8h2v2h-2L11,8zM11,11h2v2h-2v-2zM8,8h2v2L8,10L8,8zM8,11h2v2L8,13v-2zM7,13L5,13v-2h2v2zM7,10L5,10L5,8h2v2zM16,17L8,17v-2h8v2zM16,13h-2v-2h2v2zM16,10h-2L14,8h2v2zM19,13h-2v-2h2v2zM19,10h-2L17,8h2v2z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/layout/settings_with_drawer.xml b/packages/SettingsLib/res/layout/settings_with_drawer.xml
index 67296a6..a68a44e 100644
--- a/packages/SettingsLib/res/layout/settings_with_drawer.xml
+++ b/packages/SettingsLib/res/layout/settings_with_drawer.xml
@@ -41,6 +41,11 @@
android:background="?android:attr/colorPrimary" />
</FrameLayout>
<FrameLayout
+ android:id="@+id/content_header_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="?android:attr/actionBarStyle" />
+ <FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="fill_parent"
diff --git a/packages/SettingsLib/res/layout/usage_view.xml b/packages/SettingsLib/res/layout/usage_view.xml
index aa1a046..1d56668 100644
--- a/packages/SettingsLib/res/layout/usage_view.xml
+++ b/packages/SettingsLib/res/layout/usage_view.xml
@@ -71,9 +71,11 @@
android:id="@+id/bottom_label_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingStart="@dimen/usage_graph_labels_width"
android:orientation="horizontal">
-
+ <Space
+ android:id="@+id/bottom_label_space"
+ android:layout_width="@dimen/usage_graph_labels_width"
+ android:layout_height="wrap_content"/>
<include android:id="@+id/label_start"
layout="@layout/usage_side_label" />
diff --git a/packages/SettingsLib/res/values-af/arrays.xml b/packages/SettingsLib/res/values-af/arrays.xml
index 8720eb2..13c22c4 100644
--- a/packages/SettingsLib/res/values-af/arrays.xml
+++ b/packages/SettingsLib/res/values-af/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 M per logbuffer"</item>
<item msgid="5431354956856655120">"16 M per logbuffer"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Af"</item>
+ <item msgid="3054662377365844197">"Alles"</item>
+ <item msgid="688870735111627832">"Alles behalwe radio"</item>
+ <item msgid="2850427388488887328">"net kern"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Af"</item>
+ <item msgid="172978079776521897">"Alle loglêerbuffers"</item>
+ <item msgid="3873873912383879240">"Alles behalwe radiologlêerbuffers"</item>
+ <item msgid="8489661142527693381">"net kernloglêerbuffer"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animasie af"</item>
<item msgid="6624864048416710414">"Animasieskaal .5x"</item>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index eae49c4..c16e08a 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Laat toe of verbied Wi-Fi-swerfskanderings op grond van die hoeveelheid dataverkeer wat op die koppelvlak teenwoordig is"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Loggerbuffer se groottes"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Kies loggergroottes per logbuffer"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Maak logskrywer se aanhoudende berging skoon?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Wanneer ons nie meer monitering met die aanhoudende logskrywer uitvoer nie, word ons vereis om die logskrywerdata wat op jou toestel is, uit te vee."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Berg logskrywerdata aanhoudend op toestel"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Kies loglêerbuffers om aanhoudend op toestel te stoor"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Kies USB-opstelling"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Kies USB-opstelling"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Laat skynliggings toe"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Laat skynliggings toe"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Aktiveer aansigkenmerkinspeksie"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Gebruik eerder die DHCP-kliënt van Lollipop af as die nuwe Android DHCP-kliënt."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Hou mobiele data altyd aktief, selfs wanneer Wi‑Fi aktief is (vir vinnige netwerkwisseling)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Laat USB-ontfouting toe?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB-ontfouting is net vir ontwikkelingsdoeleindes bedoel. Gebruik dit om data te kopieer tussen jou rekenaar en jou toestel, programme op jou toestel te installeer sonder kennisgewing en om loglêerdata te lees."</string>
diff --git a/packages/SettingsLib/res/values-am/arrays.xml b/packages/SettingsLib/res/values-am/arrays.xml
index 62e372b..5767829 100644
--- a/packages/SettingsLib/res/values-am/arrays.xml
+++ b/packages/SettingsLib/res/values-am/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 ሜ በምዝግብ ማስታወሻ ቋጥ"</item>
<item msgid="5431354956856655120">"16 ሜ በምዝግብ ማስታወሻ ቋጥ"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"ጠፍቷል"</item>
+ <item msgid="3054662377365844197">"ሁሉም"</item>
+ <item msgid="688870735111627832">"ከሬዲዮ በስተቀር ሁሉም"</item>
+ <item msgid="2850427388488887328">"አውራ ከዋኝ ብቻ"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"ጠፍቷል"</item>
+ <item msgid="172978079776521897">"የሁሉም ምዝግብ ማስታወሻ ቋቶች"</item>
+ <item msgid="3873873912383879240">"ከሬዲዮ ምዝግብ ማስታወሻ ቋቶች በስተቀር ሁሉም"</item>
+ <item msgid="8489661142527693381">"የአውራ ከዋኝ ምዝግብ ማስታወሻ ቋት ብቻ"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"እነማ ጠፍቷል"</item>
<item msgid="6624864048416710414">"የእነማ ልኬት ለውጥ.5x"</item>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 4fd4259..6e9dcd7 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"በበይነገጹ ላይ ባለው የውሂብ ትራፊክ መጠን ላይ ተመስርተው የWi‑Fi ማንቀሳቀስ ቅኝቶችን ይፍቀዱ/ይከልክሉ"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"የምዝግብ ማስታወሻ ያዥ መጠኖች"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"በአንድ ምዝግብ ማስታወሻ ቋጥ የሚኖረው የምዝግብ ማስታወሻ ያዥ መጠኖች ይምረጡ"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"የምዝግብ ማስታወሻ ያዢ ቋሚ ማከማቻ ይጽዳ?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"ከእንግዲህ በቋሚ ምዝግብ ማስታወሻ ያዢው በማንከታተልበት ጊዜ በመሣሪያዎ ላይ የሚኖረው የምዝግብ ማስታወሻ ውሂብ መደምሰስ ይፈለግብናል።"</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"የምዝግብ ማስታወሻ ያዢ ውሂብ በመሣሪያ ላይ በቋሚነት ያከማቹ"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"በመሣሪያው ላይ በቋሚነት የሚከማች የምዝግብ ማስታወሻ ቋቶችን ይምረጡ"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"የዩኤስቢ መዋቅር ይምረጡ"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"የዩኤስቢ መዋቅር ይምረጡ"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"አስቂኝ ሥፍራዎችን ፍቀድ"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"አስቂኝ ሥፍራዎችን ፍቀድ"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"የእይታ አይነታ ምርመራን አንቃ"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"ከአዲሱ የAndroid DHCP ደንበኛ ይልቅ የLollipop DHCP ደንበኛውን ይጠቀሙ።"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"ምንም እንኳን Wi‑Fi ንቁ ቢሆንም የሞባይል ውሂብን ንቁ እንደሆነ አቆይ (ለፈጣን የአውታረ መረብ ቅይይር)።"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"የUSB ማረሚያ ይፈቀድ?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"የUSB አድስ ለግንባታ አላማ ብቻ የታሰበ ነው። ከኮምፒዩተርህ ወደ መሳሪያህ ውሂብ ለመገልበጥ፣ መሣሪያህ ላይ ያለ ማሳወቂያ መተግበሪያዎችን መጫን፣ እና ማስታወሻ ውሂብ ማንበብ ለመጠቀም ይቻላል።"</string>
diff --git a/packages/SettingsLib/res/values-ar/arrays.xml b/packages/SettingsLib/res/values-ar/arrays.xml
index 9d2a14a..e3d70fe 100644
--- a/packages/SettingsLib/res/values-ar/arrays.xml
+++ b/packages/SettingsLib/res/values-ar/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"٤ ميغابايت لكل ذاكرة تخزين مؤقت للتسجيل"</item>
<item msgid="5431354956856655120">"١٦ ميغابايت لكل ذاكرة تخزين مؤقت للتسجيل"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"إيقاف"</item>
+ <item msgid="3054662377365844197">"الكل"</item>
+ <item msgid="688870735111627832">"الكل دون اللاسلكي"</item>
+ <item msgid="2850427388488887328">"kernel فقط"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"إيقاف"</item>
+ <item msgid="172978079776521897">"كل المخازن المؤقتة للسجلات"</item>
+ <item msgid="3873873912383879240">"كل المخازن المؤقتة باستثناء مخازن سجلات اللاسلكي"</item>
+ <item msgid="8489661142527693381">"التخزين المؤقت لسجل kernel فقط"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"إيقاف الرسوم المتحركة"</item>
<item msgid="6624864048416710414">"حجم الرسوم المتحركة 0.5x"</item>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 246880c..ee579dc 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"السماح/عدم السماح بعمليات فحص Wi-Fi للتجوال بناءً على حجم حركة البيانات في الواجهة"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"أحجام ذاكرة التخزين المؤقت للتسجيل"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"حدد أحجامًا أكبر لكل ذاكرة تخزين مؤقت للتسجيل"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"هل تريد محو سعة التخزين الدائمة للمسجِّل؟"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"عندما نتوقف عن رصد أي أخطاء باستخدام المسجِّل الدائم مرة أخرى، يتعين علينا محو بيانات المسجِّل الموجودة على جهازك."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"تخزين بيانات المسجِّل باستمرار على الجهاز"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"تحديد مخازن السجلات المؤقتة المراد تخزينها باستمرار على الجهاز"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"حدد تهيئة USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"حدد تهيئة USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"السماح بمواقع وهمية"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"السماح بمواقع وهمية"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"تمكين فحص سمة العرض"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"يمكنك استخدام برنامج DHCP من Lollipop بدلاً من برنامج DHCP الجديد على Android."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"اجعل بيانات الجوّال نشطة دائمًا، حتى عندما يكون اتصال Wi‑Fi نشطًا (لتبديل الشبكة بسرعة)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"هل تريد السماح بتصحيح أخطاء USB؟"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"تم تصميم تصحيح أخطاء USB لأغراض التطوير فقط. يمكن استخدامه لنسخ البيانات بين الكمبيوتر والجهاز، وتثبيت التطبيقات على جهازك بدون تنبيه، وقراءة بيانات السجل."</string>
diff --git a/packages/SettingsLib/res/values-az-rAZ/arrays.xml b/packages/SettingsLib/res/values-az-rAZ/arrays.xml
index 682b139..396dfa1 100644
--- a/packages/SettingsLib/res/values-az-rAZ/arrays.xml
+++ b/packages/SettingsLib/res/values-az-rAZ/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"hər jurnal buferinə 4M"</item>
<item msgid="5431354956856655120">"hər jurnal buferinə 16M"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Deaktiv"</item>
+ <item msgid="3054662377365844197">"Bütün"</item>
+ <item msgid="688870735111627832">"Radiodan başqa hamısı"</item>
+ <item msgid="2850427388488887328">"yalnız kernel"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Qeyri-aktiv"</item>
+ <item msgid="172978079776521897">"Bütün loq buferləri"</item>
+ <item msgid="3873873912383879240">"Radio loq buferlərindən başqa hamısı"</item>
+ <item msgid="8489661142527693381">"yalnız kernel loq bufferi"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animasiya deaktiv"</item>
<item msgid="6624864048416710414">"Animasiya miqyası .5 x"</item>
diff --git a/packages/SettingsLib/res/values-az-rAZ/strings.xml b/packages/SettingsLib/res/values-az-rAZ/strings.xml
index 0caeea0..60d0169 100644
--- a/packages/SettingsLib/res/values-az-rAZ/strings.xml
+++ b/packages/SettingsLib/res/values-az-rAZ/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Wi‑Fi Axtarışlarına data trafikinə əsasən İcazə verin/Qadağan edin"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Logger bufer ölçüləri"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Hər jurnal buferinı Logger ölçüsü seçin"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Loqqerin davamlı yaddaşı silinsin?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Artıq davamlı loqqer ilə izləmədiyimiz zaman, cihazınızdakı loqqer data rezidentini silmək tələb olunur."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Loqqer datasını davamlı olaraq cihazda saxlayın"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Davamlı olaraq cihazda yadda saxlamaq üçün loq buferlərini seçin"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB Sazlaması seçin"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB Sazlaması seçin"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Sınaq yerləşmələrə icazə verin"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Sınaq yerləşmələrə icazə verin"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Atribut inspeksiyasına baxışa icazə verin"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Android DHCP klienti əvəzinə Lollipopdan DHCP klient istifadə edin."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Hətta Wi‑Fi aktiv olanda da mobil datanı həmişə aktiv saxlayın (sürətli şəbəkək keçidi üçün)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB debaq funksiyasına icazə verilsin?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB sazlanması yalnız inkişaf məqsədlidir. Kompüteriniz və cihazınız arasında datanı kopyalamaq üçün ondan istifadə edin, bildiriş olmadan tətbiqləri cihazınıza quraşdırın və qeydiyyat datasını oxuyun."</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
index 4715cbf..3e9f904 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 MB po međumemoriji evidencije"</item>
<item msgid="5431354956856655120">"16 MB po međumemoriji evidencije"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Isključeno"</item>
+ <item msgid="3054662377365844197">"Sve"</item>
+ <item msgid="688870735111627832">"Sve sem radija"</item>
+ <item msgid="2850427388488887328">"samo jezgro"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Isključeno"</item>
+ <item msgid="172978079776521897">"Sve međumemorije evidencija"</item>
+ <item msgid="3873873912383879240">"Sve osim međumemorija evidencija za radio"</item>
+ <item msgid="8489661142527693381">"samo međumemorija evidencije jezgra"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animacija je isključena"</item>
<item msgid="6624864048416710414">"Razmera animacije 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index a61462d..060a5fb 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Dozvoli/zabrani skeniranje Wi-Fi-ja u romingu na osnovu prisutnog protoka podataka na interfejsu"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Veličine bafera podataka u programu za evidentiranje"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Izaberite veličine po baferu evidencije"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Želite li da obrišete stalni memorijski prostor programa za evidentiranje?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Kada ih više ne nadgledamo pomoću stalnog programa za evidentiranje, dužni smo da obrišemo podatke iz programa za evidentiranje koji su trajno smešteni na uređaju."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Čuvaj evidentirane podatke na uređaju"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Izaberite međumemorije evidencije koje ćete stalno čuvati na uređaju."</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Izaberite konfiguraciju USB-a"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Izaberite konfiguraciju USB-a"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Dozvoli lažne lokacije"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Dozvoli lažne lokacije"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Omogući proveru atributa za pregled"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Koristite DHCP klijent iz Lollipop-a umesto novog Android DHCP klijenta."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Neka podaci za mobilne uređaje uvek budu aktivni, čak i kada je Wi‑Fi aktivan (radi brze promene mreže)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Dozvoli otklanjanje USB grešaka?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Otklanjanje USB grešaka namenjeno je samo za svrhe programiranja. Koristite ga za kopiranje podataka sa računara na uređaj i obrnuto, instaliranje aplikacija na uređaju bez obaveštenja i čitanje podataka iz evidencije."</string>
diff --git a/packages/SettingsLib/res/values-be-rBY/arrays.xml b/packages/SettingsLib/res/values-be-rBY/arrays.xml
index 9ac862b7..20106ef 100644
--- a/packages/SettingsLib/res/values-be-rBY/arrays.xml
+++ b/packages/SettingsLib/res/values-be-rBY/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4M на буфер журнала"</item>
<item msgid="5431354956856655120">"16M на буфер журнала"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Адключана"</item>
+ <item msgid="3054662377365844197">"Усе"</item>
+ <item msgid="688870735111627832">"Усе, акрамя радыё"</item>
+ <item msgid="2850427388488887328">"толькі ядро"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Адключана"</item>
+ <item msgid="172978079776521897">"Усе буферы журналаў"</item>
+ <item msgid="3873873912383879240">"Усе, акрамя буфераў журналаў радыё"</item>
+ <item msgid="8489661142527693381">"толькі буфер журнала ядра"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Анімацыя выключаная"</item>
<item msgid="6624864048416710414">"Маштаб анімацыі 0.5x"</item>
diff --git a/packages/SettingsLib/res/values-be-rBY/strings.xml b/packages/SettingsLib/res/values-be-rBY/strings.xml
index 5d4d688..03de8ba 100644
--- a/packages/SettingsLib/res/values-be-rBY/strings.xml
+++ b/packages/SettingsLib/res/values-be-rBY/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Дазволіць/забараніць роўмінгавае сканіраванне Wi‑Fi ў залежнасці ад аб\'ёму трафіку даных у інтэрфейсе"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Памеры буфера для сродку вядзення журнала"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Выберыце памеры сродку вядзення журнала для буфераў журнала"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Ачысціць пастаяннае сховішча журнала?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Калі перастае выконвацца адсочванне з дапамогай пастаяннага журнала, мы павінны сцерці даныя журнала, якія захоўваюцца на вашай прыладзе."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Захоўваць даныя журнала на прыл."</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Выберыце буферы журнала для пастаяннага захоўвання на прыладзе"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Выберыце канфігурацыю USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Выберыце канфігурацыю USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Дазволіць несапраўдныя месцы"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Дазволіць несапраўдныя месцы"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Уключыць прагляд атрыбутаў"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Выкарыстоўвайце кліент DHCP ад Lollipop замест новага кліента Android DHCP."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Перадача даных мабільнай сувязі заўсёды актыўна, нават калі актыўна сетка Wi‑Fi (для хуткага пераключэння паміж сеткамі)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Дазволіць адладку USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Адладка USB прызначана толькі для мэтаў распрацоўкі. Яна можа выкарыстоўвацца, каб капіяваць дадзеныя паміж кампутарам і прыладай, усталёўваць прыкладанні на прыладзе без папярэдняга апавяшчэння і чытаць дадзеныя дзённiка."</string>
diff --git a/packages/SettingsLib/res/values-bg/arrays.xml b/packages/SettingsLib/res/values-bg/arrays.xml
index 42339ef..8e803f5 100644
--- a/packages/SettingsLib/res/values-bg/arrays.xml
+++ b/packages/SettingsLib/res/values-bg/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"Рег. буфер – 4 МБ"</item>
<item msgid="5431354956856655120">"Рег. буфер – 16 МБ"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Изкл."</item>
+ <item msgid="3054662377365844197">"Всички"</item>
+ <item msgid="688870735111627832">"Вс. без радио"</item>
+ <item msgid="2850427388488887328">"само ядрото"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Изкл."</item>
+ <item msgid="172978079776521897">"Всички регистрационни буфери"</item>
+ <item msgid="3873873912383879240">"Всички регистрационни буфери освен тези за радиото"</item>
+ <item msgid="8489661142527693381">"само регистрационния буфер на ядрото"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Анимацията е изключена"</item>
<item msgid="6624864048416710414">"Скала на анимацията .5x"</item>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index efc88ca..f6596dc 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Разрешаване/забраняване на сканирането за роуминг на Wi-Fi въз основа на посочения в интерфейса обем на трафика на данни"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Размери на регистрац. буфери"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Размер на един рег. буфер: Избор"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Да се изчистят ли трайно съхраняваните регистрационни данни?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Когато спрем да извършваме наблюдения посредством програмата за трайно записване в регистрационни файлове, трябва да изтрием съхраняваните на устройството ви данни от нея."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Рег. данни да се съхр. трайно на у-вото"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Изберете кои регистрационни буфери да се съхраняват за постоянно на устройството"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Избиране на конфигурация за USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Избиране на конфигурация за USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Разрешаване на измислени местоположения"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Разрешаване на измислени местоположения"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Актив. на инспектирането на атрибутите за преглед"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Използване на клиентската програма за DHCP от Lollipop вместо новата програма на Android."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Мобилните данни са активни винаги – дори когато функцията за Wi‑Fi е включена (за бързо превключване между мрежите)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Разрешаване на отстраняването на грешки през USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Отстраняването на грешки през USB е предназначено само за програмни цели. Използвайте го за копиране на данни между компютъра и устройството си, за инсталиране на приложения на устройството си без известяване и за четене на регистрационни данни."</string>
diff --git a/packages/SettingsLib/res/values-bn-rBD/arrays.xml b/packages/SettingsLib/res/values-bn-rBD/arrays.xml
index b863934..d653a97 100644
--- a/packages/SettingsLib/res/values-bn-rBD/arrays.xml
+++ b/packages/SettingsLib/res/values-bn-rBD/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"লগ বাফার প্রতি ৪M"</item>
<item msgid="5431354956856655120">"লগ বাফার প্রতি ১৬M"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"বন্ধ আছে"</item>
+ <item msgid="3054662377365844197">"সমস্ত"</item>
+ <item msgid="688870735111627832">"সমস্ত কিন্তু রেডিও"</item>
+ <item msgid="2850427388488887328">"শুধুমাত্র কার্নেল"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"বন্ধ আছে"</item>
+ <item msgid="172978079776521897">"সমস্ত লগ বাফার"</item>
+ <item msgid="3873873912383879240">"সমস্ত কিন্তু রেডিও লগ বাফারগুলি"</item>
+ <item msgid="8489661142527693381">"শুধুমাত্র কার্নেল লগ বাফার"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"অ্যানিমেশন বন্ধ করুন"</item>
<item msgid="6624864048416710414">"অ্যানিমেশন স্কেল .৫x"</item>
diff --git a/packages/SettingsLib/res/values-bn-rBD/strings.xml b/packages/SettingsLib/res/values-bn-rBD/strings.xml
index 3717643..4a11198 100644
--- a/packages/SettingsLib/res/values-bn-rBD/strings.xml
+++ b/packages/SettingsLib/res/values-bn-rBD/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"ইন্টারফেসে উপস্থিত ডেটা ট্রাফিকের পরিমাণের উপরে ভিত্তি করে ওয়াই-ফাই রোম স্ক্যানকে অনুমোদিত/অননুমোদিত করুন"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"লগার বাফারের আকারগুলি"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"লগ বাফার প্রতি অপেক্ষাকৃত বড় আকারগুলির নির্বাচন করুন"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"লগারের সঞ্চয়স্থান সাফ করবেন?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"যেহেতু আমারা সর্বদা লগার চালু রেখে আর নিরিক্ষণ করছি না, তাই আমাদের আপনার ডিভাইসে থাকা লগার ডেটা মুছে ফেলতে হবে৷"</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"ডিভাইসে স্থায়ীভাবে লগার ডেটা সংরক্ষণ করুন"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"ডিভাইসে স্থায়ীভাবে সঞ্চয় করতে লগ বাফারগুলিকে নির্বাচন করুন"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB কনফিগারেশন নির্বাচন করুন"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB কনফিগারেশন নির্বাচন করুন"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"নকল অবস্থানের অনুমতি দিন"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"মক অবস্থানগুলি মঞ্জুর করুন"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"অ্যাট্রিবিউট পরিদর্শন দেখা সক্ষম করুন"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"নতুন Android DHCP ক্লায়েন্টের পরিবর্তে Lollipop এর থেকে DHCP ক্লায়েন্ট ব্যবহার করুন৷"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"ওয়াই-ফাই সক্রিয় থাকার সময়েও (দ্রুত নেটওয়ার্কে পাল্টানোর জন্য) সর্বদা মোবাইল ডেটা সক্রিয় রাখুন।"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB ডিবাগিং মঞ্জুর করবেন?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB ডিবাগিং কেবলমাত্র বিকাশ করার উদ্দেশ্যে। আপনার কম্পিউটার এবং আপনার ডিভাইসের মধ্যে ডেটা অনুলিপি করতে এটি ব্যবহার করুন, বিজ্ঞপ্তি ছাড়া আপনার ডিভাইসে অ্যাপ্লিকেশানগুলি ইনস্টল করুন এবং ডেটা লগ পড়ুন।"</string>
diff --git a/packages/SettingsLib/res/values-bs-rBA/arrays.xml b/packages/SettingsLib/res/values-bs-rBA/arrays.xml
index 32b8bc5..8452696 100644
--- a/packages/SettingsLib/res/values-bs-rBA/arrays.xml
+++ b/packages/SettingsLib/res/values-bs-rBA/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4M po međumemoriji dnevnika"</item>
<item msgid="5431354956856655120">"16M po međumemoriji dnevnika"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Isključeno"</item>
+ <item msgid="3054662377365844197">"Svi"</item>
+ <item msgid="688870735111627832">"Svi osim radija"</item>
+ <item msgid="2850427388488887328">"samo kernel"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Isključeno"</item>
+ <item msgid="172978079776521897">"Međuspremnici svih zapisnika"</item>
+ <item msgid="3873873912383879240">"Međuspremnici svih zapisnika osim radija"</item>
+ <item msgid="8489661142527693381">"samo međuspremnik zapisnika kernela"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animacija isključena"</item>
<item msgid="6624864048416710414">"Animacija razmjera .5x"</item>
diff --git a/packages/SettingsLib/res/values-bs-rBA/strings.xml b/packages/SettingsLib/res/values-bs-rBA/strings.xml
index 28e3507..40a3630 100644
--- a/packages/SettingsLib/res/values-bs-rBA/strings.xml
+++ b/packages/SettingsLib/res/values-bs-rBA/strings.xml
@@ -146,9 +146,9 @@
<string name="vpn_settings_not_available" msgid="956841430176985598">"VPN postavke nisu dostupne za ovog korisnika"</string>
<string name="tethering_settings_not_available" msgid="6765770438438291012">"Postavke za privezivanje nisu dostupne za ovog korisnika"</string>
<string name="apn_settings_not_available" msgid="7873729032165324000">"Postavke za naziv pristupne tačke nisu dostupne za ovog korisnika"</string>
- <string name="enable_adb" msgid="7982306934419797485">"USB otklanjanje grešaka"</string>
+ <string name="enable_adb" msgid="7982306934419797485">"Otklanjanje grešaka putem uređaja spojenog na USB"</string>
<string name="enable_adb_summary" msgid="4881186971746056635">"Način rada za uklanjanje grešaka kada je povezan USB"</string>
- <string name="clear_adb_keys" msgid="4038889221503122743">"Ukini odobrenja otklanjanja grešaka USB-om"</string>
+ <string name="clear_adb_keys" msgid="4038889221503122743">"Ukini odobrenja otklanjanja grešaka putem uređaja spojenog na USB"</string>
<string name="bugreport_in_power" msgid="7923901846375587241">"Prečica za izvještaj o greškama"</string>
<string name="bugreport_in_power_summary" msgid="1778455732762984579">"Prikaži tipku za prijavu grešaka u izborniku za potrošnju energije"</string>
<string name="keep_screen_on" msgid="1146389631208760344">"Ostani aktivan"</string>
@@ -175,16 +175,19 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Dozvoli/Zabrani Wi-Fi lutajuće skeniranje na osnovu količine podatkovnog prometa prisutnog na sučelju"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Veličine bafera za zapisnik"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Izaberite veličine za Logger prema međumemoriji evidencije"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Želite li izbrisati trajnu pohranu zapisivača?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Kada više ne pratimo trajnog zapisivača, trebamo u potpunosti izbrisati podatke zapisivača na vašem uređaju."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Trajno pohranjuj podatke zapisivača na uređaju"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Izaberite međuspremnike zapisnika za trajno pohranjivanje na uređaju"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Odaberite USB konfiguraciju"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Odaberite konfiguraciju USB-a"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Dozvoli lažne lokacije"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Dozvoli lažne lokacije"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Omogući pregled atributa prikaza"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Koristi DHCP klijent iz Lollipopa umjesto novog Android DHCP klijenta."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Uvijek drži mobilne podatke aktivnim, čak i kada je Wi-Fi je aktivan (za brzo prebacivanje između mreža)."</string>
- <string name="adb_warning_title" msgid="6234463310896563253">"Omogućiti USB otklanjanje grešaka?"</string>
- <string name="adb_warning_message" msgid="7316799925425402244">"USB otklanjanje grešaka je namijenjeno samo u svrhe razvoja aplikacija. Koristite ga za kopiranje podataka između računara i uređaja, instaliranje aplikacija na uređaj bez obavještenja te čitanje podataka iz zapisnika."</string>
- <string name="adb_keys_warning_message" msgid="5659849457135841625">"Opozvati pristup otklanjanju grešaka USB-om za sve računare koje ste prethodno ovlastili?"</string>
+ <string name="adb_warning_title" msgid="6234463310896563253">"Omogućiti otklanjanje grešaka putem uređaja spojenog na USB?"</string>
+ <string name="adb_warning_message" msgid="7316799925425402244">"Otklanjanje grešaka putem uređaja spojenog na USB je namijenjeno samo u svrhe razvoja aplikacija. Koristite ga za kopiranje podataka između računara i uređaja, instaliranje aplikacija na uređaj bez obavještenja te čitanje podataka iz zapisnika."</string>
+ <string name="adb_keys_warning_message" msgid="5659849457135841625">"Opozvati pristup otklanjanju grešaka putem uređaja spojenog na USB za sve računare koje ste prethodno ovlastili?"</string>
<string name="dev_settings_warning_title" msgid="7244607768088540165">"Dopustiti postavke za razvoj?"</string>
<string name="dev_settings_warning_message" msgid="2298337781139097964">"Ove postavke su namijenjene samo za svrhe razvoja. Mogu izazvati pogrešno ponašanje uređaja i aplikacija na njemu."</string>
<string name="verify_apps_over_usb_title" msgid="4177086489869041953">"Verifikuj aplikacije putem USB-a"</string>
diff --git a/packages/SettingsLib/res/values-ca/arrays.xml b/packages/SettingsLib/res/values-ca/arrays.xml
index 99c8a27..e4f618e 100644
--- a/packages/SettingsLib/res/values-ca/arrays.xml
+++ b/packages/SettingsLib/res/values-ca/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 M / memòria intermèdia reg."</item>
<item msgid="5431354956856655120">"16 M / memòria intermèdia reg."</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Desactivat"</item>
+ <item msgid="3054662377365844197">"Tot"</item>
+ <item msgid="688870735111627832">"Tot menys mòbil"</item>
+ <item msgid="2850427388488887328">"només kernel"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Desactivat"</item>
+ <item msgid="172978079776521897">"Totes les memòries intermèdies de registre"</item>
+ <item msgid="3873873912383879240">"Tot menys mem. interm. de registre de senyal mòbil"</item>
+ <item msgid="8489661142527693381">"només memòria intermèdia del registre de kernel"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animació desactivada"</item>
<item msgid="6624864048416710414">"Escala d\'animació 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 2487313..99850a5 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -111,7 +111,7 @@
<string name="tts_play_example_summary" msgid="8029071615047894486">"Reprodueix una breu demostració de síntesi de veu"</string>
<string name="tts_install_data_title" msgid="4264378440508149986">"Instal·la dades de veu"</string>
<string name="tts_install_data_summary" msgid="5742135732511822589">"Instal·la les dades de veu necessàries per a la síntesi de veu"</string>
- <string name="tts_engine_security_warning" msgid="8786238102020223650">"Pot ser que aquest motor de síntesi de la parla pugui recopilar tot el text que es dirà en veu alta, incloses les dades personals, com ara les contrasenyes i els números de les targetes de crèdit. Ve del motor <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Voleu activar l\'ús d\'aquest motor de síntesi de la parla?"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Pot ser que aquest motor de síntesi de la parla pugui recopilar tot el text que es dirà en veu alta, incloses les dades personals, com ara les contrasenyes i els números de les targetes de crèdit. Ve del motor <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Vols activar l\'ús d\'aquest motor de síntesi de la parla?"</string>
<string name="tts_engine_network_required" msgid="1190837151485314743">"Aquest idioma requereix una connexió de xarxa activa per a la sortida de síntesi de veu."</string>
<string name="tts_default_sample_string" msgid="4040835213373086322">"Això és un exemple de síntesi de veu"</string>
<string name="tts_status_title" msgid="7268566550242584413">"Estat de l\'idioma predeterminat"</string>
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Permet/No permetis cerques de xarxes Wi-Fi en itinerància basades en la quantitat de dades presents a la interfície"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Mides memòria intermèdia Logger"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Mida Logger per memòria intermèdia"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Vols esborrar l\'emmagatzematge persistent del registrador?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Quan deixem de supervisar amb el registrador persistent, hem d\'esborrar les dades del registrador que hi ha al teu dispositiu."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Emm. dades reg. persist. a disp."</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Selecciona memòries interm. de registre per emmag. de manera persistent al disp."</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Selecciona configuració USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Selecciona configuració USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Ubicacions simulades"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Permet les ubicacions simulades"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Inspecció d\'atributs de visualització"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Utilitza el client DHCP de Lollipop en lloc del nou client DHCP d\'Android"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Mantén les dades mòbils sempre actives, fins i tot quan la Wi‑Fi està activada (per canviar de xarxa ràpidament)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Voleu permetre la depuració USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"La depuració USB només està indicada per a activitats de desenvolupament. Fes-la servir intercanviar dades entre l\'ordinador i el dispositiu, per instal·lar aplicacions al dispositiu sense rebre notificacions i per llegir dades de registre."</string>
diff --git a/packages/SettingsLib/res/values-cs/arrays.xml b/packages/SettingsLib/res/values-cs/arrays.xml
index 8953485..b0185e8 100644
--- a/packages/SettingsLib/res/values-cs/arrays.xml
+++ b/packages/SettingsLib/res/values-cs/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 MB na vyrovnávací paměť protokolů"</item>
<item msgid="5431354956856655120">"16 MB na vyrovnávací paměť protokolů"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Vypnuto"</item>
+ <item msgid="3054662377365844197">"Vše"</item>
+ <item msgid="688870735111627832">"Bezdrát. ne"</item>
+ <item msgid="2850427388488887328">"pouze jádro"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Vypnuto"</item>
+ <item msgid="172978079776521897">"Všechny vyrovnávací paměti protokolů"</item>
+ <item msgid="3873873912383879240">"Všechny kromě protokolu bezdrátových modulů"</item>
+ <item msgid="8489661142527693381">"pouze protokol vyrovnávací paměti jádra"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animace je vypnuta"</item>
<item msgid="6624864048416710414">"Měřítko animace 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 42c7bdf..361ba1f 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Povolí nebo zakáže Wi-Fi roaming na základě množství datového provozu na rozhraní."</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Vyrovnávací paměť protokol. nástroje"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Velikost vyrovnávací paměti protokol. nástroje"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Vymazat trvalé úložiště protokolovacího nástroje?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Pokud již pomocí nástroje na trvalé protokolování nic nemonitorujeme, jsme povinni jeho data uložená v zařízení vymazat."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Ukládat data protokolovacích nástrojů trvale do zařízení"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Vyberte, které vyrovnávací paměti protokolů chcete trvale ukládat do zařízení"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Výběr konfigurace USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Výběr konfigurace USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Povolit simulované polohy"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Povolit simulované polohy"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Kontrola atributu zobrazení"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Namísto nového klientu DHCP Android použít klient DHCP z verze Lollipop."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Mobilní data budou vždy ponechána aktivní, i když bude aktivní Wi-Fi (za účelem rychlého přepínání sítí)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Povolit ladění USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Ladění prostřednictvím rozhraní USB je určeno pouze pro účely vývoje. Použijte je ke kopírování dat mezi počítačem a zařízením, instalaci aplikací do zařízení bez upozornění a čtení dat protokolů."</string>
diff --git a/packages/SettingsLib/res/values-da/arrays.xml b/packages/SettingsLib/res/values-da/arrays.xml
index d700c05..d06f39f 100644
--- a/packages/SettingsLib/res/values-da/arrays.xml
+++ b/packages/SettingsLib/res/values-da/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 MB pr. logbuffer"</item>
<item msgid="5431354956856655120">"16 MB pr. logbuffer"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Slået fra"</item>
+ <item msgid="3054662377365844197">"Alle"</item>
+ <item msgid="688870735111627832">"Radio undtaget"</item>
+ <item msgid="2850427388488887328">"kun kerne"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Slået fra"</item>
+ <item msgid="172978079776521897">"Alle logbuffere"</item>
+ <item msgid="3873873912383879240">"Alle undtagen radiologbuffere"</item>
+ <item msgid="8489661142527693381">"kun logbuffer for kerne"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animation fra"</item>
<item msgid="6624864048416710414">"Animationsskala 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 8f8d7a6..98f41fc 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Tillad/forbyd scanning i forbindelse med Wi-Fi-roaming afhængigt af mængden af datatrafik i grænsefladen"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Størrelser for Logger-buffer"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Vælg Logger-størrelser pr. logbuffer"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Vil du rydde det permanente lager for logger?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Når vi ikke længere overvåger via den permanente logger, er vi nødt til at slette de logdata, der er gemt på din enhed."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Gem logdata permanent på enheden"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Vælg logbuffere, der skal gemmes permanent på enheden"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Vælg USB-konfiguration"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Vælg USB-konfiguration"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Imiterede placeringer"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Tillad imiterede placeringer"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Aktivér visning af attributinspektion"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Brug DHCP-klienten fra Lollipop i stedet for den nye DHCP-klient i Android."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Hold altid mobildata aktiveret, selv når Wi-Fi er aktiveret (for at skifte hurtigt mellem netværk)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Vil du tillade USB-fejlretning?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB-fejlretning er kun beregnet til udvikling og kan bruges til at kopiere data mellem din computer og enheden, installere apps på enheden uden meddelelser og læse logdata."</string>
diff --git a/packages/SettingsLib/res/values-de/arrays.xml b/packages/SettingsLib/res/values-de/arrays.xml
index a9c802d..9a81e8f 100644
--- a/packages/SettingsLib/res/values-de/arrays.xml
+++ b/packages/SettingsLib/res/values-de/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 Mio. pro Puffer"</item>
<item msgid="5431354956856655120">"16 Mio. pro Puffer"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Aus"</item>
+ <item msgid="3054662377365844197">"Alle"</item>
+ <item msgid="688870735111627832">"Alle außer Funkschnittstelle"</item>
+ <item msgid="2850427388488887328">"Nur Kernel"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Aus"</item>
+ <item msgid="172978079776521897">"Alle Protokollzwischenspeicher"</item>
+ <item msgid="3873873912383879240">"Alle Protokollzwischenspeicher außer Funkschnittstelle"</item>
+ <item msgid="8489661142527693381">"Nur Kernelprotokoll-Zwischenspeicher"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animation aus"</item>
<item msgid="6624864048416710414">"Animationsmaßstab: 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 169d6b0..c9da6c9 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"WLAN-Roamingsuchen je nach Umfang des Datentraffics an der Schnittstelle zulassen bzw. nicht zulassen"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Logger-Puffergrößen"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Größe pro Protokollpuffer wählen"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Speicher der dauerhaften Protokollierung löschen?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Wenn keine Überwachung über eine dauerhafte Protokollierung mehr stattfindet, sind wir dazu verpflichtet, die Protokolldaten auf deinem Gerät zu löschen."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Protokolldaten dauerhaft speichern"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Protokollzwischenspeicher zum dauerhaften Speichern auf Gerät auswählen"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB-Konfiguration auswählen"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB-Konfiguration auswählen"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Simulierte Standorte"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Simulierte Standorte zulassen"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Inspektion der Anzeigeattribute aktivieren"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"DHCP-Client von Lollipop statt des neuen Android-DHCP-Clients verwenden"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Die mobile Datennutzung bleibt auch dann aktiviert, wenn WLAN aktiviert ist. Dies dient einem schnelleren Wechsel zwischen Netzwerken."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB-Debugging zulassen?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB-Debugging ist nur für Entwicklungszwecke vorgesehen. Damit kannst du Daten zwischen deinem Computer und deinem Gerät kopieren, Apps auf deinem Gerät ohne Benachrichtigung installieren und Protokolldaten lesen."</string>
@@ -284,8 +287,8 @@
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Wechseln…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Dateiverschlüsselung wird bereits verwendet."</string>
<string name="title_convert_fbe" msgid="1263622876196444453">"Zu Dateiverschlüsselung wechseln"</string>
- <string name="convert_to_fbe_warning" msgid="6139067817148865527">"Stelle von Datenpartitions- auf dateibasierte Verschlüsselung um.\n !!Achtung!! Dadurch werden alle deine Daten gelöscht.\n Es handelt sich um eine Alphaversion, die möglicherweise nicht korrekt funktioniert.\n Wähle \"Wischen und wechseln…\" aus, um fortzufahren."</string>
- <string name="button_convert_fbe" msgid="5152671181309826405">"Wischen und wechseln…"</string>
+ <string name="convert_to_fbe_warning" msgid="6139067817148865527">"Du möchtest von Datenpartitions- zur dateibasierten Verschlüsselung wechseln.\nAchtung! Dadurch werden alle deine Daten gelöscht.\nEs handelt sich um eine Alphaversion, die möglicherweise nicht korrekt funktioniert.\nWähle \"Löschen und wechseln…\" aus, um fortzufahren."</string>
+ <string name="button_convert_fbe" msgid="5152671181309826405">"Löschen und wechseln…"</string>
<string name="picture_color_mode" msgid="4560755008730283695">"Farbmodus für Bilder"</string>
<string name="picture_color_mode_desc" msgid="1141891467675548590">"sRGB verwenden"</string>
<string name="daltonizer_mode_disabled" msgid="7482661936053801862">"Deaktiviert"</string>
diff --git a/packages/SettingsLib/res/values-el/arrays.xml b/packages/SettingsLib/res/values-el/arrays.xml
index 91f9d6a..a6ce80a 100644
--- a/packages/SettingsLib/res/values-el/arrays.xml
+++ b/packages/SettingsLib/res/values-el/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 M ανά προσ. μν. αρχ. καταγρ."</item>
<item msgid="5431354956856655120">"16 M ανά πρ. μν. αρχ. καταγρ."</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Ανενεργό"</item>
+ <item msgid="3054662377365844197">"Όλα"</item>
+ <item msgid="688870735111627832">"Όλοι εκτός από του αποστολέα"</item>
+ <item msgid="2850427388488887328">"μόνο πυρήνας"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Ανενεργό"</item>
+ <item msgid="172978079776521897">"Όλα τα αρχεία καταγραφής προσωρινής μνήμης"</item>
+ <item msgid="3873873912383879240">"Όλα εκτός από τα αρχεία κατ.προσ.μνήμης αποστολέα"</item>
+ <item msgid="8489661142527693381">"μόνο προσωρινή μνήμη αρχείου καταγραφής πυρήνα"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Κινούμ.εικόνες απενεργοποιημένες"</item>
<item msgid="6624864048416710414">"Κλίμ. κινούμ. εικ. 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index c525145..05e4e64 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Να επιτρέπεται/να μην επιτρέπεται η σάρωση Wi-Fi κατά την περιαγωγή, βάσει της ποσότητας επισκεψιμότητας δεδομένων στη διεπαφή"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Μέγεθος προσωρινής μνήμης για τη λειτουργία καταγραφής"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Μέγεθος αρχείων κατ/φής ανά προ/νή μνήμη αρχείου κατ/φής"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Διαγραφή αποθηκευτικού χώρου μόνιμων αρχείων καταγραφής;"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Όταν δεν γίνεται πλέον παρακολούθηση με μόνιμο αρχείο καταγραφής, θα πρέπει να διαγραφούν τα δεδομένα του αρχείου καταγραφής στη συσκευή σας."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Αποθ.δεδ.αρχείων κατ.μόνιμα στη συσκ."</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Επιλογή αρχείων καταγραφής προσωρινής μνήμης για αποθήκευση μόνιμα στη συσκευή"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Επιλογή διαμόρφωσης USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Επιλογή διαμόρφωσης USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Να επιτρέπονται ψευδείς τοποθεσίες"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Να επιτρέπονται ψευδείς τοποθεσίες"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Ενεργοποίηση του ελέγχου χαρακτηριστικών προβολής"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Χρήση εφαρμογής-πελάτη DHCP παλαιού τύπου από το Lollipop αντί για τη νέα εφαρμογή-πελάτη DHCP Android."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Τα δεδομένα κινητής τηλεφωνίας να διατηρούνται πάντα ενεργά, ακόμα και όταν είναι ενεργό το Wi-Fi (για γρήγορη εναλλαγή δικτύου)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Να επιτρέπεται ο εντοπισμός σφαλμάτων USB;"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Ο εντοπισμός σφαλμάτων USB προορίζεται μόνο για σκοπούς προγραμματισμού. Χρησιμοποιήστε τον για αντιγραφή δεδομένων μεταξύ του υπολογιστή και της συσκευής σας, για την εγκατάσταση εφαρμογών στη συσκευή σας χωρίς προειδοποίηση και για την ανάγνωση δεδομένων καταγραφής."</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/arrays.xml b/packages/SettingsLib/res/values-en-rAU/arrays.xml
index 05518900..2ee613f 100644
--- a/packages/SettingsLib/res/values-en-rAU/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rAU/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 M per log buffer"</item>
<item msgid="5431354956856655120">"16 M per log buffer"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Off"</item>
+ <item msgid="3054662377365844197">"All"</item>
+ <item msgid="688870735111627832">"All but radio"</item>
+ <item msgid="2850427388488887328">"kernel only"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Off"</item>
+ <item msgid="172978079776521897">"All log buffers"</item>
+ <item msgid="3873873912383879240">"All but radio log buffers"</item>
+ <item msgid="8489661142527693381">"kernel log buffer only"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animation off"</item>
<item msgid="6624864048416710414">"Animation scale .5x"</item>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 05fc9db..799802b 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Allow/Disallow Wi‑Fi Roam Scans based on the amount of data traffic present at the interface"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Logger buffer sizes"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Select Logger sizes per log buffer"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Clear logger persistent storage?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"When we are no longer monitoring with the persistent logger, we are required to erase the logger data resident on your device."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Store logger data persistently on device"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Select log buffers to store persistently on device"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Select USB Configuration"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Select USB Configuration"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Allow mock locations"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Allow mock locations"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Enable view attribute inspection"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Use the DHCP client from Lollipop instead of the new Android DHCP client."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Always keep mobile data active, even when Wi‑Fi is active (for fast network switching)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Allow USB debugging?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB debugging is intended for development purposes only. Use it to copy data between your computer and your device, install apps on your device without notification and read log data."</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/arrays.xml b/packages/SettingsLib/res/values-en-rGB/arrays.xml
index 05518900..2ee613f 100644
--- a/packages/SettingsLib/res/values-en-rGB/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rGB/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 M per log buffer"</item>
<item msgid="5431354956856655120">"16 M per log buffer"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Off"</item>
+ <item msgid="3054662377365844197">"All"</item>
+ <item msgid="688870735111627832">"All but radio"</item>
+ <item msgid="2850427388488887328">"kernel only"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Off"</item>
+ <item msgid="172978079776521897">"All log buffers"</item>
+ <item msgid="3873873912383879240">"All but radio log buffers"</item>
+ <item msgid="8489661142527693381">"kernel log buffer only"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animation off"</item>
<item msgid="6624864048416710414">"Animation scale .5x"</item>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 05fc9db..799802b 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Allow/Disallow Wi‑Fi Roam Scans based on the amount of data traffic present at the interface"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Logger buffer sizes"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Select Logger sizes per log buffer"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Clear logger persistent storage?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"When we are no longer monitoring with the persistent logger, we are required to erase the logger data resident on your device."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Store logger data persistently on device"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Select log buffers to store persistently on device"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Select USB Configuration"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Select USB Configuration"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Allow mock locations"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Allow mock locations"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Enable view attribute inspection"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Use the DHCP client from Lollipop instead of the new Android DHCP client."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Always keep mobile data active, even when Wi‑Fi is active (for fast network switching)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Allow USB debugging?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB debugging is intended for development purposes only. Use it to copy data between your computer and your device, install apps on your device without notification and read log data."</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/arrays.xml b/packages/SettingsLib/res/values-en-rIN/arrays.xml
index 05518900..2ee613f 100644
--- a/packages/SettingsLib/res/values-en-rIN/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rIN/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 M per log buffer"</item>
<item msgid="5431354956856655120">"16 M per log buffer"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Off"</item>
+ <item msgid="3054662377365844197">"All"</item>
+ <item msgid="688870735111627832">"All but radio"</item>
+ <item msgid="2850427388488887328">"kernel only"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Off"</item>
+ <item msgid="172978079776521897">"All log buffers"</item>
+ <item msgid="3873873912383879240">"All but radio log buffers"</item>
+ <item msgid="8489661142527693381">"kernel log buffer only"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animation off"</item>
<item msgid="6624864048416710414">"Animation scale .5x"</item>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 05fc9db..799802b 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Allow/Disallow Wi‑Fi Roam Scans based on the amount of data traffic present at the interface"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Logger buffer sizes"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Select Logger sizes per log buffer"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Clear logger persistent storage?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"When we are no longer monitoring with the persistent logger, we are required to erase the logger data resident on your device."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Store logger data persistently on device"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Select log buffers to store persistently on device"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Select USB Configuration"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Select USB Configuration"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Allow mock locations"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Allow mock locations"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Enable view attribute inspection"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Use the DHCP client from Lollipop instead of the new Android DHCP client."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Always keep mobile data active, even when Wi‑Fi is active (for fast network switching)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Allow USB debugging?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB debugging is intended for development purposes only. Use it to copy data between your computer and your device, install apps on your device without notification and read log data."</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/arrays.xml b/packages/SettingsLib/res/values-es-rUS/arrays.xml
index 1b7a9f0..eae9b1b 100644
--- a/packages/SettingsLib/res/values-es-rUS/arrays.xml
+++ b/packages/SettingsLib/res/values-es-rUS/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 M/búfer registro"</item>
<item msgid="5431354956856655120">"16 M/búfer registro"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Desactivado"</item>
+ <item msgid="3054662377365844197">"Todo"</item>
+ <item msgid="688870735111627832">"Excepto radio"</item>
+ <item msgid="2850427388488887328">"solo kernel"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Desactivado"</item>
+ <item msgid="172978079776521897">"Todos los búferes de registro"</item>
+ <item msgid="3873873912383879240">"Todos los búferes de registro, excepto radio"</item>
+ <item msgid="8489661142527693381">"solo búfer de registro de kernel"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animación desactivada"</item>
<item msgid="6624864048416710414">"Escala de animación 0.5x"</item>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 2fd5e9b..08e739a 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Permitir/no permitir las búsquedas de Wi-Fi basadas la cantidad de tráfico de datos presente en la interfaz"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Tamaños de búfer de Logger"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Selecciona el tamaño del Logger por búfer"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"¿Borrar el almacenamiento persistente del registrador?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Cuando ya no usamos el registrador persistente para monitorear, debemos borrar los datos que almacena el registrador en tu dispositivo."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Almacenar de forma persistente"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Selecciona búferes de registro para almacenamiento persistente en dispositivo"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Seleccionar configuración de USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Seleccionar configuración de USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Ubicaciones de prueba"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Permitir ubicaciones de prueba"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Habilitar inspección de atributos de vista"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Usar el cliente DHCP de Lollipop en lugar del nuevo cliente DHCP de Android"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Siempre mantén los datos móviles activos, incluso cuando esté activada la conexión Wi‑Fi (para cambiar de red de forma rápida)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"¿Permitir depuración por USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"La depuración por USB solo está indicada para actividades de programación. Úsala para copiar datos entre tu computadora y el dispositivo, para instalar aplicaciones en el dispositivo sin recibir notificaciones y para leer datos de registro."</string>
diff --git a/packages/SettingsLib/res/values-es/arrays.xml b/packages/SettingsLib/res/values-es/arrays.xml
index a64e873..168f378 100644
--- a/packages/SettingsLib/res/values-es/arrays.xml
+++ b/packages/SettingsLib/res/values-es/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 M/búfer registro"</item>
<item msgid="5431354956856655120">"16 M/búfer registro"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"No"</item>
+ <item msgid="3054662377365844197">"Todo"</item>
+ <item msgid="688870735111627832">"Todo menos señal móvil"</item>
+ <item msgid="2850427388488887328">"solo kernel"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"No"</item>
+ <item msgid="172978079776521897">"Todos los búferes de registro"</item>
+ <item msgid="3873873912383879240">"Todo excepto búferes de registro de señal móvil"</item>
+ <item msgid="8489661142527693381">"solo búfer de registro del kernel"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animación desactivada"</item>
<item msgid="6624864048416710414">"Escala de animación 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index c6cddd9..3d2f6c1 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Permitir/No permitir búsquedas de Wi-Fi basadas en la cantidad de tráfico de datos presente en la interfaz"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Tamaños del búfer de Logger"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Elige el tamaño del Logger por búfer"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"¿Borrar almacenamiento continuo del registrador?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Cuando ya no supervisamos la actividad con el registrador de forma continua, estamos obligados a borrar los datos del registrador almacenados en el dispositivo."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Guardar datos de forma continua"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Seleccionar búferes de registro para guardarlos de forma continua en dispositivo"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Seleccionar configuración de USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Seleccionar configuración de USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Ubicaciones simuladas"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Permitir ubicaciones simuladas"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Inspección de atributos de vista"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Utiliza el cliente DHCP de Lollipop en lugar del nuevo cliente DHCP de Android."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Mantén los datos móviles siempre activos, aunque la conexión Wi‑Fi esté activada (para cambiar de red rápidamente)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"¿Permitir depuración por USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"La depuración por USB solo está indicada para actividades de desarrollo. Puedes utilizarla para intercambiar datos entre el ordenador y el dispositivo, para instalar aplicaciones en el dispositivo sin recibir notificaciones y para leer datos de registro."</string>
diff --git a/packages/SettingsLib/res/values-et-rEE/arrays.xml b/packages/SettingsLib/res/values-et-rEE/arrays.xml
index 9731983..019bf50 100644
--- a/packages/SettingsLib/res/values-et-rEE/arrays.xml
+++ b/packages/SettingsLib/res/values-et-rEE/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 000 000 / logipuhver"</item>
<item msgid="5431354956856655120">"16 000 000 / logipuhver"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Väljas"</item>
+ <item msgid="3054662377365844197">"Kõik"</item>
+ <item msgid="688870735111627832">"Kõik, v.a raadio"</item>
+ <item msgid="2850427388488887328">"ainult tuum"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Väljas"</item>
+ <item msgid="172978079776521897">"Kõik logi puhvrid"</item>
+ <item msgid="3873873912383879240">"Kõik, v.a raadiologi puhvrid"</item>
+ <item msgid="8489661142527693381">"ainult tuuma logi puhver"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animatsioon väljas"</item>
<item msgid="6624864048416710414">"Animatsiooni skaala 0,5 korda"</item>
diff --git a/packages/SettingsLib/res/values-et-rEE/strings.xml b/packages/SettingsLib/res/values-et-rEE/strings.xml
index cda3d9f..a7b0f64 100644
--- a/packages/SettingsLib/res/values-et-rEE/strings.xml
+++ b/packages/SettingsLib/res/values-et-rEE/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Luba/keela WiFi-rändluse skannimine liidese andmeliikluse põhjal"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Logija puhvri suurused"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Vali logija suur. logipuhvri kohta"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Kas kustutada logija püsivalt salvestatud andmed?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Kui me ei kasuta jälgimiseks enam püsivat logijat, peame teie seadmes asuvad logijaandmed eemaldama."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Salvesta logijaandmed püsivalt seadmesse"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Valige logi puhvrid, mis püsivalt seadmesse salvestada"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB-seadistuse valimine"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB-seadistuse valimine"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Luba võltsasukohti"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Luba võltsasukohti"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Luba kuva atribuudi hindamine"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Kasutage uue Androidi DHCP-kliendi asemel Lollipopi DHCP-klienti."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Hoidke mobiilne andmeside alati aktiivsena, isegi kui WiFi on aktiivne (võrkude kiireks vahetamiseks)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Luban USB silumise?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB-silumine on mõeldud ainult arendamiseks. Kasutage seda andmete kopeerimiseks oma arvuti ja seadme vahel, seadmesse rakenduste installimiseks ilma teatisteta ning logiandmete lugemiseks."</string>
diff --git a/packages/SettingsLib/res/values-eu-rES/arrays.xml b/packages/SettingsLib/res/values-eu-rES/arrays.xml
index b72b537..c617445 100644
--- a/packages/SettingsLib/res/values-eu-rES/arrays.xml
+++ b/packages/SettingsLib/res/values-eu-rES/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 M erregistroen bufferreko"</item>
<item msgid="5431354956856655120">"16 M erregistroen bufferreko"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Desaktibatuta"</item>
+ <item msgid="3054662377365844197">"Guztiak"</item>
+ <item msgid="688870735111627832">"Guztiak, irratiarenak izan ezik"</item>
+ <item msgid="2850427388488887328">"kernela soilik"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Desaktibatuta"</item>
+ <item msgid="172978079776521897">"Erregistroen buffer guztiak"</item>
+ <item msgid="3873873912383879240">"Erregistroen buffer guztiak, irratiarenak izan ezik"</item>
+ <item msgid="8489661142527693381">"kernelaren erregistroaren bufferra soilik"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animazioa desaktibatuta"</item>
<item msgid="6624864048416710414">"Animazio-eskala: 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-eu-rES/strings.xml b/packages/SettingsLib/res/values-eu-rES/strings.xml
index 76f7798..f476511 100644
--- a/packages/SettingsLib/res/values-eu-rES/strings.xml
+++ b/packages/SettingsLib/res/values-eu-rES/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Onartu edo debekatu ibiltaritzan Wi-Fi sareak bilatzea, interfazeko datu-trafikoaren arabera"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Erregistroen buffer-tamainak"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Hautatu erregistroen buffer-tamainak"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Erregistro iraunkorraren biltegia garbitu nahi duzu?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Erregistro iraunkor hau kontrolatzeari uzten diogunean gailuan dituzun erregistroko datuak ezabatu beharko ditugu."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Utzi datuak gailuan gordeta"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Hautatu gailuan gordeta utzi nahi dituzun erregistroen bufferrak"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Hautatu USB konfigurazioa"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Hautatu USB konfigurazioa"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Onartu kokapen faltsuak"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Onartu kokapen faltsuak"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Gaitu ikuspegiaren atributuak ikuskatzeko aukera"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Erabili Lollipop bertsioko DHCP bezeroa eta ez Android DHCP bezero berria."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Mantendu mugikorreko datuak beti aktibo, baita Wi-Fi konexioa aktibo dagoenean ere (sarez bizkor aldatu ahal izateko)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB arazketa onartu?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB arazketa garapen-xedeetarako soilik dago diseinatuta. Erabil ezazu ordenagailuaren eta gailuaren artean datuak kopiatzeko, aplikazioak gailuan jakinarazi gabe instalatzeko eta erregistro-datuak irakurtzeko."</string>
diff --git a/packages/SettingsLib/res/values-fa/arrays.xml b/packages/SettingsLib/res/values-fa/arrays.xml
index e597592..6dc8491 100644
--- a/packages/SettingsLib/res/values-fa/arrays.xml
+++ b/packages/SettingsLib/res/values-fa/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"۴ میلیون در هر بافر گزارش"</item>
<item msgid="5431354956856655120">"۱۶ میلیون در هر بافر گزارش"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"خاموش"</item>
+ <item msgid="3054662377365844197">"همه"</item>
+ <item msgid="688870735111627832">"همه بهجز رادیو"</item>
+ <item msgid="2850427388488887328">"فقط هسته اصلی"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"خاموش"</item>
+ <item msgid="172978079776521897">"همه بافرهای گزارش"</item>
+ <item msgid="3873873912383879240">"همه بهجز بافرهای گزارش رادیو"</item>
+ <item msgid="8489661142527693381">"فقط بافر گزارش هسته اصلی"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"پویانمایی خاموش"</item>
<item msgid="6624864048416710414">"مقیاس پویانمایی 0.5x"</item>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 8729ffd..4491851 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"مجاز/غیرمجاز کردن اسکنهای رومینگ Wi‑Fi براساس مقدار ترافیک داده موجود در واسط"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"اندازههای حافظه موقت ثبتکننده"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"انتخاب اندازه ثبتکننده در حافظه موقت ثبت"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"حافظه دائم ثبتکننده پاک شود؟"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"وقتی دیگر با ثبتکننده دائم پایش نمیکنیم، باید دادههای ثبتکننده موجود در دستگاهتان را پاک کنیم."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"ذخیره دائم داده ثبتکننده در دستگاه"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"انتخاب بافرهای گزارش برای ذخیره دائم در دستگاه"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"انتخاب پیکربندی USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"انتخاب پیکربندی USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"مکانهای کاذب مجاز هستند"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"مکانهای کاذب مجاز هستند"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"فعال کردن بازبینی ویژگی بازدید"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"به جای کلاینت Android DHCP جدید، از کلاینت Lollipop DHCP استفاده کنید."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"داده سلولی همیشه فعال نگه داشته میشود، حتی وقتی Wi-Fi فعال است (برای جابهجایی سریع شبکه)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"اشکالزدایی USB انجام شود؟"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"اشکالزدایی USB فقط برای اهداف برنامهنویسی در نظر گرفته شده است. از آن برای رونوشتبرداری داده بین رایانه و دستگاهتان، نصب برنامهها در دستگاهتان بدون اعلان و خواندن دادههای گزارش استفاده کنید."</string>
diff --git a/packages/SettingsLib/res/values-fi/arrays.xml b/packages/SettingsLib/res/values-fi/arrays.xml
index 15e6e40..3c2fe61 100644
--- a/packages/SettingsLib/res/values-fi/arrays.xml
+++ b/packages/SettingsLib/res/values-fi/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 Mt / lokipuskuri"</item>
<item msgid="5431354956856655120">"16 Mt / lokipuskuri"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Ei käytössä"</item>
+ <item msgid="3054662377365844197">"Kaikki"</item>
+ <item msgid="688870735111627832">"Kaikki paitsi radio"</item>
+ <item msgid="2850427388488887328">"vain kernel"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Ei käytössä"</item>
+ <item msgid="172978079776521897">"Kaikki lokipuskurit"</item>
+ <item msgid="3873873912383879240">"Kaikki paitsi radiolokipuskurit"</item>
+ <item msgid="8489661142527693381">"vain kernel-lokipuskuri"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animaatio pois käytöstä"</item>
<item msgid="6624864048416710414">"Animaatioasteikko 0,5-kertainen"</item>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 5c4287a..cdef968 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Salli/estä Wi-Fi-verkkovierailuskannaus liittymässä esiintyvän dataliikenteen perusteella."</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Lokipuskurien koot"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Valitse puskurikohtaiset lokikoot"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Tyhjennetäänkö lokityökalun pysyvä tallennustila?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Kun lopetamme valvonnan laitteella pysyvästi olevalla lokityökalulla, meidän täytyy poistaa kaikki laitteellesi tallennetut lokityökalun tiedot."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Säilytä lokityökalun tietoja laitteella"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Valitse lokipuskurit, joita säilytetään pysyvästi laitteella"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Valitse USB-määritykset"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Valitse USB-määritykset"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Salli sijaintien imitointi"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Salli sijaintien imitointi"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Ota attribuuttinäkymän tarkistus käyttöön"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Käytä Lollipopin DHCP-asiakassovellusta Androidin uuden DHCP-asiakassovelluksen sijasta."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Pidä mobiilidata aina käytössä, vaikka Wi-Fi olisi aktiivinen. Tämä mahdollistaa nopeamman vaihtelun verkkojen välillä."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Sallitaanko USB-vianetsintä?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB-vianetsintä on tarkoitettu vain kehittäjien käyttöön. Sen avulla voidaan kopioida tietoja tietokoneesi ja laitteesi välillä, asentaa laitteeseesi sovelluksia ilmoittamatta siitä sinulle ja lukea lokitietoja."</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/arrays.xml b/packages/SettingsLib/res/values-fr-rCA/arrays.xml
index ab48103..ba25d27 100644
--- a/packages/SettingsLib/res/values-fr-rCA/arrays.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 Mo/tampon journal"</item>
<item msgid="5431354956856655120">"16 Mo/tampon journal"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Désactivé"</item>
+ <item msgid="3054662377365844197">"Tous"</item>
+ <item msgid="688870735111627832">"Tous sauf radio"</item>
+ <item msgid="2850427388488887328">"noyau uniquement"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Désactivé"</item>
+ <item msgid="172978079776521897">"Tous les tampons de journal"</item>
+ <item msgid="3873873912383879240">"Tous les tampons de journal sauf celui de la radio"</item>
+ <item msgid="8489661142527693381">"tampon journal du noyau uniquement"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animation désactivée"</item>
<item msgid="6624864048416710414">"Échelle d\'animation 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 6f7982d..958b8fd 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Autoriser ou non la détection de réseaux Wi-Fi en itinérance en fonction de l\'importance du transfert de données dans l\'interface"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Tailles des mémoires tampons d\'enregistreur"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Tailles enreg. par tampon journal"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Supprimer les données de l\'enregistreur?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Lorsque nous n\'effectuons plus de suivi avec l\'enregistreur persistant, nous sommes tenus d\'effacer les données de l\'enregistreur qui se trouvent sur votre appareil."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Enreg. données journ. pers. sur appareil"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Sélect. les tampons de journal à stocker de manière persistante sur l\'appareil"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Sélectionnez une configuration USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Sélectionnez une configuration USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Autoriser les positions fictives"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Autoriser les positions fictives"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Activer l\'inspection d\'attribut d\'affichage"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Utiliser le client DHCP de Lollipop au lieu du nouveau client DHCP Android."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Toujours garder les données cellulaires actives, même lorsque le Wi-Fi est activé (pour la commutation rapide entre les réseaux)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Autoriser le débogage USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Le débogage USB est conçu uniquement pour le développement. Utilisez-le pour copier des données entre votre ordinateur et votre appareil, installer des applications sur votre appareil sans notification et lire les données de journal."</string>
diff --git a/packages/SettingsLib/res/values-fr/arrays.xml b/packages/SettingsLib/res/values-fr/arrays.xml
index dd828c1..3340afb 100644
--- a/packages/SettingsLib/res/values-fr/arrays.xml
+++ b/packages/SettingsLib/res/values-fr/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 Mo par tampon journal"</item>
<item msgid="5431354956856655120">"16 Mo par tampon journal"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Désactivé"</item>
+ <item msgid="3054662377365844197">"Tous"</item>
+ <item msgid="688870735111627832">"Tous sauf radio"</item>
+ <item msgid="2850427388488887328">"noyau uniquement"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Désactivé"</item>
+ <item msgid="172978079776521897">"Toutes les mémoires tampon journal"</item>
+ <item msgid="3873873912383879240">"Toutes sauf les mémoires tampon journal radio"</item>
+ <item msgid="8489661142527693381">"tampon journal du noyau uniquement"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animation désactivée"</item>
<item msgid="6624864048416710414">"Échelle d\'animation x 0,5"</item>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index a72ecd6..cf08f3b 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Autoriser ou non la détection de réseaux Wi-Fi en itinérance en fonction de l\'importance du trafic de données dans l\'interface"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Tailles mémoires tampons enregistr."</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Tailles enreg. par tampon journal"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Effacer l\'espace de stockage persistant de l\'enregistreur ?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Lorsque nous n\'effectuons plus de suivi avec l\'enregistreur persistant, nous sommes tenus d\'effacer les données associées à ce dernier qui sont stockées sur votre appareil."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Stocker données enregistreur en permanence sur appareil"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Sélectionner les mémoires tampon journal à stocker en permanence sur l\'appareil"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Sélectionner une configuration USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Sélectionner une configuration USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Positions fictives"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Autoriser les positions fictives"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Activer inspect. attribut affich."</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Utiliser le client DHCP de Lollipop au lieu du nouveau client DHCP Android"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Maintenir l\'état actif des données mobiles, même lorsque le Wi‑Fi est actif (pour changer rapidement de réseau)"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Autoriser le débogage USB ?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Le débogage USB est conçu uniquement pour le développement. Utilisez-le pour copier des données entre votre ordinateur et votre appareil, installer des applications sur votre appareil sans notification et lire les données de journal."</string>
diff --git a/packages/SettingsLib/res/values-gl-rES/arrays.xml b/packages/SettingsLib/res/values-gl-rES/arrays.xml
index 8c2ed06..201cda2 100644
--- a/packages/SettingsLib/res/values-gl-rES/arrays.xml
+++ b/packages/SettingsLib/res/values-gl-rES/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 M por búfer de rexistro"</item>
<item msgid="5431354956856655120">"16 M por búfer de rexistro"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Desactivado"</item>
+ <item msgid="3054662377365844197">"Todo"</item>
+ <item msgid="688870735111627832">"Agás radio"</item>
+ <item msgid="2850427388488887328">"só kernel"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Desactivado"</item>
+ <item msgid="172978079776521897">"Todos os búfers de rexistro"</item>
+ <item msgid="3873873912383879240">"Todo agás os búfers de rexistro de radio"</item>
+ <item msgid="8489661142527693381">"só búfer do rexistro do kernel"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animación desactivada"</item>
<item msgid="6624864048416710414">"Escala de animación 0,5x"</item>
@@ -115,10 +127,10 @@
<item msgid="3414540279805870511">"720 p (seguro)"</item>
<item msgid="9039818062847141551">"1080 p"</item>
<item msgid="4939496949750174834">"1080 p (seguro)"</item>
- <item msgid="1833612718524903568">"4 K"</item>
- <item msgid="238303513127879234">"4 K (seguro)"</item>
- <item msgid="3547211260846843098">"4 K (mellorado)"</item>
- <item msgid="5411365648951414254">"4 K (mellorado e seguro)"</item>
+ <item msgid="1833612718524903568">"4K"</item>
+ <item msgid="238303513127879234">"4K (seguro)"</item>
+ <item msgid="3547211260846843098">"4K (mellorado)"</item>
+ <item msgid="5411365648951414254">"4K (mellorado e seguro)"</item>
<item msgid="1311305077526792901">"720 p, 1080 p (pantalla dual)"</item>
</string-array>
<string-array name="enable_opengl_traces_entries">
diff --git a/packages/SettingsLib/res/values-gl-rES/strings.xml b/packages/SettingsLib/res/values-gl-rES/strings.xml
index 8fb4431..cdc4581 100644
--- a/packages/SettingsLib/res/values-gl-rES/strings.xml
+++ b/packages/SettingsLib/res/values-gl-rES/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Permitir/Non permitir buscas de itinerancia da wifi baseadas na cantidade de tráfico de datos presente na interface"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Tamaños de búfer de rexistrador"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Seleccionar tamaños por búfer"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Queres borrar o almacenamento continuo do rexistrador?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Cando xa non se supervisa a actividade co rexistrador de forma continua, debemos borrar os datos do rexistrador almacenados no dispositivo."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Gardar datos de forma continua"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Seleccionar búfers de rexistro para gardalos de forma continua no dispositivo"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Seleccionar configuración USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Seleccionar configuración USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Permitir localizacións falsas"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Permite localizacións falsas"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Activar a inspección de atributos de visualización"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Utiliza o cliente DHCP de Lollipop en lugar do novo cliente DHCP de Android."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Mantén sempre os datos móbiles activos, aínda que a wifi estea activada (para un rápido cambio de rede)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Queres permitir a depuración USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"A depuración de erros USB está deseñada unicamente para fins de programación. Utilízaa para copiar datos entre o ordenador e o dispositivo, instalar aplicacións no dispositivo sen enviar notificacións e ler os datos do rexistro."</string>
diff --git a/packages/SettingsLib/res/values-gu-rIN/arrays.xml b/packages/SettingsLib/res/values-gu-rIN/arrays.xml
index 92cbb2d..c746188 100644
--- a/packages/SettingsLib/res/values-gu-rIN/arrays.xml
+++ b/packages/SettingsLib/res/values-gu-rIN/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"લૉગ બફર દીઠ 4M"</item>
<item msgid="5431354956856655120">"લૉગ બફર દીઠ 16M"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"બંધ"</item>
+ <item msgid="3054662377365844197">"તમામ"</item>
+ <item msgid="688870735111627832">"તમામ પરંતુ રેડિઓ"</item>
+ <item msgid="2850427388488887328">"ફક્ત કર્નલ"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"બંધ"</item>
+ <item msgid="172978079776521897">"તમામ લૉગ બફર્સ"</item>
+ <item msgid="3873873912383879240">"તમામ પરંતુ રેડિઓ લૉગ બફર્સ"</item>
+ <item msgid="8489661142527693381">"ફક્ત કર્નલ લૉગ બફર"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"એનિમેશન બંધ"</item>
<item msgid="6624864048416710414">"એનિમેશન સ્કેલ .5x"</item>
diff --git a/packages/SettingsLib/res/values-gu-rIN/strings.xml b/packages/SettingsLib/res/values-gu-rIN/strings.xml
index fcb3968..1b40e01 100644
--- a/packages/SettingsLib/res/values-gu-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-gu-rIN/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"ઇન્ટરફેસ પર હાજર ડેટા ટ્રાફિકના પ્રમાણનાં આધારે Wi‑Fi રોમ સ્કૅન્સને મંજૂરી આપો/નામંજૂર કરો"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"લોગર બફર કદ"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"લૉગ દીઠ લૉગર કદ બફર પસંદ કરો"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"લૉગર નિરંતર સ્ટોરેજ સાફ કરીએ?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"જ્યારે અમે હવે નિરંતર લૉગર સાથે મોનીટર કરતાં નથી, તો તમારા ઉપકરણ પર રહેલો લૉગર ડેટા કાઢી નાખવાની જરૂર છે."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"તમારા ઉપકરણ પર લૉગર ડેટા નિરંતર સંગ્રહિત કરો"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"તમારા ઉપકરણ પર નિરંતર સંગ્રહવા માટે લૉગ બફર પસંદ કરો"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB ગોઠવણી પસંદ કરો"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB ગોઠવણી પસંદ કરો"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"મોક સ્થાનોની મંજૂરી આપો"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"મોક સ્થાનોની મંજૂરી આપો"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"લક્ષણ નિરીક્ષણ જોવાનું સક્ષમ કરો"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"નવા Android DHCP ક્લાઇન્ટને બદલે Lollipop પરના DHCP ક્લાઇન્ટનો ઉપયોગ કરો."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Wi‑Fi સક્રિય હોય ત્યારે પણ, હંમેશા મોબાઇલ ડેટાને સક્રિય રાખો (ઝડપી નેટવર્ક સ્વિચિંગ માટે)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB ડિબગિંગને મંજૂરી આપીએ?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB ડિબગીંગ ફક્ત વિકાસ હેતુઓ માટે જ બનાવાયેલ છે. તેનો ઉપયોગ તમારા કમ્પ્યુટર અને તમારા ઉપકરણ વચ્ચે ડેટાને કૉપિ કરવા, સૂચના વગર તમારા ઉપકરણ પર ઍપ્લિકેશનો ઇન્સ્ટોલ કરવા અને લૉગ ડેટા વાંચવા માટે કરો."</string>
diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml
index 8aa98a1f..29b6bef 100644
--- a/packages/SettingsLib/res/values-hi/arrays.xml
+++ b/packages/SettingsLib/res/values-hi/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4M प्रति लॉग बफ़र"</item>
<item msgid="5431354956856655120">"16M प्रति लॉग बफ़र"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"बंद"</item>
+ <item msgid="3054662377365844197">"सभी"</item>
+ <item msgid="688870735111627832">"रेडियो छोड़कर सभी"</item>
+ <item msgid="2850427388488887328">"केवल कर्नल"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"बंद"</item>
+ <item msgid="172978079776521897">"सभी लॉग बफ़र"</item>
+ <item msgid="3873873912383879240">"रेडियो लॉग बफ़र को छोड़कर सभी"</item>
+ <item msgid="8489661142527693381">"केवल कर्नल लॉग बफ़र"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"एनिमेशन बंद"</item>
<item msgid="6624864048416710414">"एनिमेशन स्केल .5x"</item>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 65338cc..8dfbb4a 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"इंटरफ़ेस पर वर्तमान में मौजूद डेटा ट्रैफ़िक के आधार पर वाई-फ़ाई रोम स्कैन करने देता/नहीं देता है"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"लॉगर बफ़र आकार"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"प्रति लॉग बफ़र लॉगर आकार चुनें"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"लॉगर सतत मेमोरी साफ़ करें?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"जब हम सतत लॉगर के साथ निगरानी करना बंद कर देते हैं, तो हमें आपके डिवाइस पर मौजूद लॉगर डेटा को मिटाने की आवश्यकता होती है."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"डिवाइस पर लॉगर डेटा सतत संग्रहीत करें"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"डिवाइस पर सतत रूप से संग्रहीत करने के लिए लॉग बफ़र चुनें"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB कॉन्फ़िगरेशन चुनें"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB कॉन्फ़िगरेशन चुनें"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"कृत्रिम स्थानों को अनुमति दें"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"कृत्रिम स्थानों को अनुमति दें"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"दृश्य विशेषता निरीक्षण सक्षम करें"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"नए Android DHCP क्लाइंट के बजाय Lollipop के DHCP क्लाइंट का उपयोग करें."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"वाई-फ़ाई के सक्रिय रहने पर भी, हमेशा मोबाइल डेटा सक्रिय रखें (तेज़ी से नेटवर्क स्विच करने के लिए)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB डीबग करने की अनुमति दें?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB डीबग डीबग करने का उद्देश्य केवल विकास है. इसका उपयोग आपके कंप्यूटर और आपके डिवाइस के बीच डेटा की प्रतिलिपि बनाने, बिना नोटिफिकेशन के आपके डिवाइस पर ऐप्स इंस्टॉल करने और लॉग डेटा पढ़ने के लिए करें."</string>
diff --git a/packages/SettingsLib/res/values-hr/arrays.xml b/packages/SettingsLib/res/values-hr/arrays.xml
index db460c7..a2d253c 100644
--- a/packages/SettingsLib/res/values-hr/arrays.xml
+++ b/packages/SettingsLib/res/values-hr/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 MB po međusprem. zapisnika"</item>
<item msgid="5431354956856655120">"16 MB po međusprem. zapisnika"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Isključeno"</item>
+ <item msgid="3054662377365844197">"Sve"</item>
+ <item msgid="688870735111627832">"Sve osim radija"</item>
+ <item msgid="2850427388488887328">"samo jezgra"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Isključeno"</item>
+ <item msgid="172978079776521897">"Svi međuspremnici zapisa"</item>
+ <item msgid="3873873912383879240">"Sve osim međuspremnika zapisnika radija"</item>
+ <item msgid="8489661142527693381">"samo međuspremnik zapisnika jezgre"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animacija isključena"</item>
<item msgid="6624864048416710414">"Brzina animacije 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 974af2c..07e4925 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Dopustite ili blokirajte slobodno traženje Wi-Fi mreža na temelju količine podatkovnog prometa na sučelju."</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Veličine međuspremnika zapisnika"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Odaberite veličinu međuspremnika zapisnika"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Želite li izbrisati trajnu pohranu zapisivača?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Kad prekinemo praćenje pomoću trajnog zapisivača, morat ćemo izbrisati podatke zapisivača koji se nalaze na uređaju."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Pohrani podatke zapisivača na uređaju"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Odaberite međuspremnike zapisnika koji će se trajno pohraniti na uređaju"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Odabir USB konfiguracije"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Odabir USB konfiguracije"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Dopusti probne lokacije"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Dopusti probne lokacije"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Omogući pregled atributa prikaza"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Upotrebljavajte DHCP klijent iz Lollipopa umjesto novog Android DHCP klijenta."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Neka mobilni podaci uvijek budu aktivni, čak i kada je Wi‑Fi aktivan (za brzo prebacivanje s jedne na drugu mrežu)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Omogućiti otklanjanje pogrešaka putem USB-a?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Otklanjanje pogrešaka putem USB-a namijenjeno je samo u razvojne svrhe. Može se upotrijebiti za kopiranje podataka s računala na uređaj i obrnuto, instalaciju aplikacija na uređaju bez obavijesti i za čitanje dnevničkih zapisa."</string>
diff --git a/packages/SettingsLib/res/values-hu/arrays.xml b/packages/SettingsLib/res/values-hu/arrays.xml
index 0d201b5..41ce4b0 100644
--- a/packages/SettingsLib/res/values-hu/arrays.xml
+++ b/packages/SettingsLib/res/values-hu/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 MB/naplópuffer"</item>
<item msgid="5431354956856655120">"16 MB/naplópuffer"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Kikapcsolva"</item>
+ <item msgid="3054662377365844197">"Összes"</item>
+ <item msgid="688870735111627832">"Rádiót kivéve"</item>
+ <item msgid="2850427388488887328">"csak kernel"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Kikapcsolva"</item>
+ <item msgid="172978079776521897">"Minden naplópuffer"</item>
+ <item msgid="3873873912383879240">"A rádiónapló-pufferen kívül mindegyik"</item>
+ <item msgid="8489661142527693381">"csak kernelnaplópuffer"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animáció ki"</item>
<item msgid="6624864048416710414">"Animáció tempója: 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 059a8bb..0d8ec26 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"A Wi-Fi-roaming ellenőrzésének engedélyezése vagy letiltása az interfészen jelen lévő adatforgalom mennyiségétől függően"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Naplózási puffer mérete"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Naplózási pufferméret kiválasztása"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Törli a naplózó program állandó tárhelyét?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Ha az állandó naplózó programot már nem használjuk tovább, kötelesek vagyunk törölni a naplózott adatokat eszközéről."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Naplózott adatok állandó tárolása"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Válassza ki az állandó tárolásra szánt naplópuffereket"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB-beállítás kiválasztása"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB-beállítás kiválasztása"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Helyutánzatok engedélyezése"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Helyutánzatok engedélyezése"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Nézetattribútum vizsgálatának engedélyezése"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"A Lollipop DHCP-kliensének használata az új androidos DHCP-kliens helyett."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"A mobiladat-kapcsolat mindig maradjon aktív, még akkor is, ha a Wi‑Fi aktív (a gyors hálózatváltás érdekében)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Engedélyezi az USB hibakeresést?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Az USB hibakeresés fejlesztési célokat szolgál. Használhatja adatok másolására a számítógép és a készülék között, alkalmazások a készülékre való értesítés nélküli telepítésére és naplózási adatok olvasására."</string>
@@ -323,7 +326,7 @@
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Rendszergazda által irányítva"</string>
<string name="enabled_by_admin" msgid="2386503803463071894">"Engedélyezve a rendszergazda által"</string>
<string name="disabled_by_admin" msgid="3669999613095206948">"Letiltva a rendszergazda által"</string>
- <string name="home" msgid="3256884684164448244">"Kezdőlap beállítása"</string>
+ <string name="home" msgid="3256884684164448244">"Beállítások kezdőlapja"</string>
<string-array name="battery_labels">
<item msgid="8494684293649631252">"0%"</item>
<item msgid="8934126114226089439">"50%"</item>
diff --git a/packages/SettingsLib/res/values-hy-rAM/arrays.xml b/packages/SettingsLib/res/values-hy-rAM/arrays.xml
index 3e477cd..e93cbfc 100644
--- a/packages/SettingsLib/res/values-hy-rAM/arrays.xml
+++ b/packages/SettingsLib/res/values-hy-rAM/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"Պահնակ՝ առավ. 4ՄԲ"</item>
<item msgid="5431354956856655120">"Պահնակ՝ առավ. 16ՄԲ"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Անջատված է"</item>
+ <item msgid="3054662377365844197">"Բոլորը"</item>
+ <item msgid="688870735111627832">"Բոլորը, ռադիոյից բացի"</item>
+ <item msgid="2850427388488887328">"միայն միջուկը"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Անջատված է"</item>
+ <item msgid="172978079776521897">"Մատյանի բոլոր բուֆերները"</item>
+ <item msgid="3873873912383879240">"Մատյանի բոլոր բուֆերները, ռադիո բուֆերներից բացի"</item>
+ <item msgid="8489661142527693381">"միայն միջուկի մատյանի պահնակը"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Անջատել շարժապատկերը"</item>
<item msgid="6624864048416710414">"Շարժապատկերի սանդղակը` .5x"</item>
diff --git a/packages/SettingsLib/res/values-hy-rAM/strings.xml b/packages/SettingsLib/res/values-hy-rAM/strings.xml
index 70118de..cb12fbf 100644
--- a/packages/SettingsLib/res/values-hy-rAM/strings.xml
+++ b/packages/SettingsLib/res/values-hy-rAM/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Թույլատրել/արգելել Wi‑Fi ռոումինգի որոնումը՝ կախված միջերեսում տվյալների երթևեկի ծավալից"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Տեղեկամատյանի պահնակի չափերը"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Ընտրեք տեղեկամատյանի չափը մեկ պահնակի համար"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Ջնջե՞լ մատյանի մշտական հիշողությունը:"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Մշտական տվյալների գրանցման մատյանի միջոցով վերահսկողությունը դադարեցնելու դեպքում մենք պարտավոր ենք ջնջել մատյանի տվյալները, որոնք պահվում են ձեր սարքում:"</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Պահել մատյանի տվյալները սարքի մշտական հիշողությունում"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Ընտրեք մատյանի բուֆերներ՝ սարքի մշտական հիշողությունում պահելու համար"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Ընտրեք USB կարգավորումը"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Ընտրեք USB կարգավորումը"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Թույատրել կեղծ տեղադրությունները"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Թույլ տալ կեղծ տեղադրություններ"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Միացնել ցուցադրման հատկանիշների ստուգումը"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Օգտագործել Lollipop-ի DHCP ծրագիրը՝ նոր Android DHCP ծրագրի փոխարեն:"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Միշտ ակտիվացրած պահել բջջային տվյալները, նույնիսկ Wi‑Fi-ը միացրած ժամանակ (ցանցերի միջև արագ փոխարկման համար):"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Թույլատրե՞լ USB-ի վրիպազերծումը:"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB վրիպազերծումը միայն ծրագրավորման նպատակների համար է: Օգտագործեք այն ձեր համակարգչից տվյալները ձեր սարք պատճենելու համար, առանց ծանուցման ձեր սարքի վրա ծրագրեր տեղադրելու և տվյալների մատյանը ընթերցելու համար:"</string>
diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml
index 166efd5..a612639 100644
--- a/packages/SettingsLib/res/values-in/arrays.xml
+++ b/packages/SettingsLib/res/values-in/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 M/penyangga log"</item>
<item msgid="5431354956856655120">"16 M/penyangga log"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Nonaktif"</item>
+ <item msgid="3054662377365844197">"Semua"</item>
+ <item msgid="688870735111627832">"Selain radio"</item>
+ <item msgid="2850427388488887328">"khusus kernel"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Nonaktif"</item>
+ <item msgid="172978079776521897">"Semua penyangga log"</item>
+ <item msgid="3873873912383879240">"Semua kecuali penyangga log radio"</item>
+ <item msgid="8489661142527693381">"khusus penyangga log kernel"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animasi mati"</item>
<item msgid="6624864048416710414">"Skala animasi 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index acbbb4c..ffdb607 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Izinkan/Larang Pemindaian Roaming Wi-Fi berdasarkan jumlah lalu lintas data yang ada di antarmuka"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Ukuran penyangga pencatat log"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Ukuran Pencatat Log per penyangga log"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Hapus penyimpanan tetap pencatat log?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Jika kami tidak memantau lagi dengan pencatat log tetap, kami diwajibkan menghapus data pencatat log yang ada di perangkat Anda."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Terus simpan data pencatat log di perangkat"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Pilih penyangga log untuk terus menyimpan di perangkat"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Pilih Konfigurasi USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Pilih Konfigurasi USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Mengizinkan lokasi palsu"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Mengizinkan lokasi palsu"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Aktifkan inspeksi atribut tampilan"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Gunakan klien DHCP dari Lollipop alih-alih klien DHCP Android baru."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Selalu aktifkan data seluler, meski Wi-Fi aktif (agar jaringan beralih dengan cepat)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Izinkan melakukan debug USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Debugging USB dimaksudkan untuk tujuan pengembangan saja. Gunakan untuk menyalin data antara komputer dan perangkat Anda, memasang apl pada perangkat tanpa notifikasi, dan membaca data log."</string>
diff --git a/packages/SettingsLib/res/values-is-rIS/arrays.xml b/packages/SettingsLib/res/values-is-rIS/arrays.xml
index 6024e30..64d4da2 100644
--- a/packages/SettingsLib/res/values-is-rIS/arrays.xml
+++ b/packages/SettingsLib/res/values-is-rIS/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 M/biðminni"</item>
<item msgid="5431354956856655120">"16 M/biðminni"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Slökkt"</item>
+ <item msgid="3054662377365844197">"Allt"</item>
+ <item msgid="688870735111627832">"Allt n. útvarp"</item>
+ <item msgid="2850427388488887328">"einungis kjarni"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Slökkt"</item>
+ <item msgid="172978079776521897">"Allt biðminni"</item>
+ <item msgid="3873873912383879240">"Allt nema útvarpsbiðminni"</item>
+ <item msgid="8489661142527693381">"einungis kjarnaannálabiðminni"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Slökkt á hreyfiáhrifum"</item>
<item msgid="6624864048416710414">"Lengd hreyfiáhrifa 5x"</item>
diff --git a/packages/SettingsLib/res/values-is-rIS/strings.xml b/packages/SettingsLib/res/values-is-rIS/strings.xml
index 4bfc0a6..25e13a4 100644
--- a/packages/SettingsLib/res/values-is-rIS/strings.xml
+++ b/packages/SettingsLib/res/values-is-rIS/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Leyfa/banna reikileit með Wi-Fi á grunni þess hversu mikil gagnaumferð er fyrir hendi í viðmótinu"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Annálsritastærðir biðminna"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Veldu annálsritastærðir á biðminni"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Hreinsa varanlega geymslu annálsrita?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Þegar við notum ekki lengur varanlegan annálsrita til að fylgjast með þá þurfum við að eyða annálsritanum af tækinu þínu."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Geyma annálsgögn í tækinu"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Veldu biðminni fyrir varanlega geymslu í tækinu"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Velja USB-stillingar"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Velja USB-stillingar"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Leyfa gervistaðsetningar"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Leyfa gervistaðsetningar"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Virkja yfirlit skoðunar eiginda"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Nota gamla DHCP-biðlarann úr Lollipop í staðinn fyrir nýja Android DHCP-biðlarann."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Hafa alltaf kveikt á farsímagögnum, líka þegar kveikt er á Wi-Fi (til að skipta megi hratt milli kerfa)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Leyfa USB-villuleit?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB-villuleit er aðeins ætluð til nota í þróunarskyni. Hana má nota til að afrita gögn á milli tölvu og tækis, setja forrit upp í tækinu án tilkynninga og lesa annálagögn."</string>
diff --git a/packages/SettingsLib/res/values-it/arrays.xml b/packages/SettingsLib/res/values-it/arrays.xml
index be7acfc..e6938e0 100644
--- a/packages/SettingsLib/res/values-it/arrays.xml
+++ b/packages/SettingsLib/res/values-it/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 MB/buffer log"</item>
<item msgid="5431354956856655120">"16 MB/buffer log"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Off"</item>
+ <item msgid="3054662377365844197">"Tutti"</item>
+ <item msgid="688870735111627832">"Tutti tranne il segnale radio"</item>
+ <item msgid="2850427388488887328">"solo kernel"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Off"</item>
+ <item msgid="172978079776521897">"Tutti i buffer log"</item>
+ <item msgid="3873873912383879240">"Tutti tranne i buffer log del segnale radio"</item>
+ <item msgid="8489661142527693381">"solo buffer log kernel"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animazione disattivata"</item>
<item msgid="6624864048416710414">"Scala animazione 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 959b661..cb323a2 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Consenti/vieta scansioni roaming Wi-Fi basate sulla quantità di traffico dati presente a livello di interfaccia"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Dimensioni buffer Logger"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Seleziona dimensioni Logger per buffer log"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Cancellare i dati nello spazio di archiviazione permanente del logger?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Quando il monitoraggio tramite logger permanente viene interrotto, siamo obbligati a eliminare i dati del logger memorizzati sul dispositivo."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Salva dati del logger sul dispositivo in modo permanente"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Seleziona i buffer log da memorizzare in modo permanente sul dispositivo"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Seleziona configurazione USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Seleziona configurazione USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Posizioni fittizie"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Consenti posizioni fittizie"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Attiva controllo attributi visualizzazione"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Utilizza il client DHCP di Lollipop anziché il nuovo client DHCP Android."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Mantieni sempre i dati cellulare attivi, anche se il Wi‑Fi è attivo (per un passaggio fra reti rapido)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Consentire debug USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Il debug USB è solo a scopo di sviluppo. Utilizzalo per copiare dati tra il computer e il dispositivo, per installare applicazioni sul tuo dispositivo senza notifica e per leggere i dati dei log."</string>
@@ -325,7 +328,7 @@
<string name="disabled_by_admin" msgid="3669999613095206948">"Disattivata dall\'amministratore"</string>
<string name="home" msgid="3256884684164448244">"Home page Impostazioni"</string>
<string-array name="battery_labels">
- <item msgid="8494684293649631252">"0%%"</item>
+ <item msgid="8494684293649631252">"0%"</item>
<item msgid="8934126114226089439">"50%%"</item>
<item msgid="1286113608943010849">"100%%"</item>
</string-array>
diff --git a/packages/SettingsLib/res/values-iw/arrays.xml b/packages/SettingsLib/res/values-iw/arrays.xml
index 01127c6..7bd46ad 100644
--- a/packages/SettingsLib/res/values-iw/arrays.xml
+++ b/packages/SettingsLib/res/values-iw/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4M לכל מאגר יומן"</item>
<item msgid="5431354956856655120">"16M לכל מאגר יומן"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"כבוי"</item>
+ <item msgid="3054662377365844197">"הכול"</item>
+ <item msgid="688870735111627832">"הכול מלבד רדיו"</item>
+ <item msgid="2850427388488887328">"ליבה בלבד"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"כבוי"</item>
+ <item msgid="172978079776521897">"כל מאגרי הנתונים הזמניים של היומן"</item>
+ <item msgid="3873873912383879240">"הכול מלבד מאגרי הנתונים הזמניים של יומן הרדיו"</item>
+ <item msgid="8489661142527693381">"מאגר נתונים זמני של היומן השמור בליבה בלבד"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"אנימציה כבויה"</item>
<item msgid="6624864048416710414">"קנה מידה לאנימציה 5x."</item>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 44016ef..7c16e42 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"התר/מנע סריקות נדידה של Wi-Fi בהתבסס על נפח תנועת הנתונים הקיימת בממשק"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"גדלי מאגר של יוצר יומן"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"בחר גדלים של יוצר יומן לכל מאגר יומן"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"האם למחוק את אחסון המתעד המתמיד?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"כשאנחנו כבר לא מבצעים מעקב באמצעות המתעד המתמיד, אנחנו נדרשים למחוק את נתוני המתעד המקומי במכשיר."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"אחסון נתוני מתעד מתמיד במכשיר"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"בחר מאגר נתונים זמני ליומן לשם אחסון מתמיד במכשיר"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"בחר תצורת USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"בחר תצורת USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"אפשר מיקומים מדומים"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"אפשר מיקומים מדומים"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"אפשר בדיקת תכונת תצוגה"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"השתמש בלקוח DHCP של Lollipop במקום לקוח DHCP החדש של Android."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"השאר את הנתונים לנייד פעילים תמיד, גם כש-Wi‑Fi פעיל (למעבר מהיר בין רשתות)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"לאפשר ניפוי באגים של USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"ניפוי באגים באמצעות USB מיועד למטרות פיתוח בלבד. השתמש בו להעתקת נתונים בין המחשב והמכשיר שלך, להתקנת אפליקציות במכשיר ללא התראה ולקריאת נתוני יומן."</string>
diff --git a/packages/SettingsLib/res/values-ja/arrays.xml b/packages/SettingsLib/res/values-ja/arrays.xml
index 1246377..5b9b41c 100644
--- a/packages/SettingsLib/res/values-ja/arrays.xml
+++ b/packages/SettingsLib/res/values-ja/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 M / ログバッファ"</item>
<item msgid="5431354956856655120">"16 M / ログバッファ"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"OFF"</item>
+ <item msgid="3054662377365844197">"すべて"</item>
+ <item msgid="688870735111627832">"ラジオ以外すべて"</item>
+ <item msgid="2850427388488887328">"カーネルのみ"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"OFF"</item>
+ <item msgid="172978079776521897">"すべてのログバッファ"</item>
+ <item msgid="3873873912383879240">"ラジオ ログバッファ以外すべて"</item>
+ <item msgid="8489661142527693381">"カーネル ログ バッファのみ"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"アニメーションオフ"</item>
<item msgid="6624864048416710414">"アニメーションスケール.5x"</item>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 12c5e25..25f4152 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"インターフェースのデータトラフィック量に基づいたWi-Fiローミングスキャンを許可するかしないかを設定できます"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"ログバッファのサイズ"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"各ログバッファのログサイズを選択"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"ログの永続ストレージを消去しますか?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Google が常駐ロガーで監視していない場合、端末上のログデータを消去する必要があります。"</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"ログデータを端末上に永続的に保存"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"端末上に永続的に保存するログバッファを選択"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB設定の選択"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB設定の選択"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"擬似ロケーションを許可"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"擬似ロケーションを許可する"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"表示属性検査を有効にする"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"新しいAndroid DHCPクライアントの代わりにLollipopのDHCPクライアントを使用します。"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Wi‑Fiが(ネットワークの自動切り替えで)ONのときでもモバイルデータが常にONになります。"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USBデバッグを許可しますか?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USBデバッグは開発専用に設計されています。パソコンと端末の間でデータをコピーする場合や、アプリを通知なしで端末にインストールする場合、ログデータを読み取る場合に使用できます。"</string>
diff --git a/packages/SettingsLib/res/values-ka-rGE/arrays.xml b/packages/SettingsLib/res/values-ka-rGE/arrays.xml
index 4b6f78b..5e63ddd 100644
--- a/packages/SettingsLib/res/values-ka-rGE/arrays.xml
+++ b/packages/SettingsLib/res/values-ka-rGE/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 მბაიტი / ჟურნალის ბუფერი"</item>
<item msgid="5431354956856655120">"16 მბაიტი / ჟურნალის ბუფერი"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"გამორთული"</item>
+ <item msgid="3054662377365844197">"ყველა"</item>
+ <item msgid="688870735111627832">"რადიოს გარდა ყველა"</item>
+ <item msgid="2850427388488887328">"მხოლოდ ბირთვი"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"გამორთული"</item>
+ <item msgid="172978079776521897">"ჟურნალების ყველა ბუფერი"</item>
+ <item msgid="3873873912383879240">"რადიოჟურნალების ბუფერების გარდა ყველა"</item>
+ <item msgid="8489661142527693381">"მხოლოდ ბირთვის ჟურნალის ბუფერი"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"ანიმაციის გამორთვა"</item>
<item msgid="6624864048416710414">"ანიმაციის სიჩქარე .5x"</item>
diff --git a/packages/SettingsLib/res/values-ka-rGE/strings.xml b/packages/SettingsLib/res/values-ka-rGE/strings.xml
index 5ac2382..ffe194a 100644
--- a/packages/SettingsLib/res/values-ka-rGE/strings.xml
+++ b/packages/SettingsLib/res/values-ka-rGE/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Wifi Roam სკანირების დაშვება/აკრძალვა, ინტერფეისზე არსებული მონაცემთა ტრაფიკზე დაფუძნებით"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"ჟურნალიზაციის ბუფერის ზომები"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"აირჩიეთ ჟურნ. ზომა / ჟურნ. ბუფერზე"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"გსურთ მუდმივი ჟურნალირების მეხსიერების გასუფთავება?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"მუდმივი ჟურნალირების მეშვეობით მონიტორინგის შეწყვეტის შემდეგ, იძულებული ვიქნებით, თქვენს მოწყობილობაზე არსებული ჟურნალების მონაცემები წავშალოთ."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"მოწყობილობაზე ჟურნალირების მონაცემების მუდმივ რეჟიმში შენახვა"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"აირჩიეთ ჟურნალების ბუფერები, რომლებიც მოწყობილობაზე მუდმივ რეჟიმში უნდა შეინახოს"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"აირჩიეთ USB კონფიგურაცია"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"აირჩიეთ USB კონფიგურაცია"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"ფიქტიური მდებარეობების დაშვება"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"ფიქტიური მდებარეობების დაშვება"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"ნახვის ატრიბუტის ინსპექტირების ჩართვა"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"ახალი Android DHCP კლიენტის ნაცვლად, Lollipop-ის DHCP კლიენტის გამოყენება."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"მობილური მოწყობილობის მონაცემები ყოველთვის აქტიური დარჩეს, მაშინაც კი, როდესაც Wi-Fi აქტიურია (ქსელის სწრაფი გადართვისთვის)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"ჩაირთოს USB გამართვა?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB გამართვა განკუთვნილია მხოლოდ დეველოპერული მიზნებისთვის. გამოიყენეთ კომპიუტერსა და თქვენ მოწყობილობას შორის მონაცემების გადასატანად, თქვენ მოწყობილობაზე აპების შეტყობინების გარეშე დასაყენებლად და ჟურნალის მონაცემების წასაკითხად."</string>
diff --git a/packages/SettingsLib/res/values-kk-rKZ/arrays.xml b/packages/SettingsLib/res/values-kk-rKZ/arrays.xml
index 43afb2f..d1dae87 100644
--- a/packages/SettingsLib/res/values-kk-rKZ/arrays.xml
+++ b/packages/SettingsLib/res/values-kk-rKZ/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"Әр журнал буферіне 4 МБ"</item>
<item msgid="5431354956856655120">"Әр журнал буферіне 16 МБ"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Өшірулі"</item>
+ <item msgid="3054662377365844197">"Барлығы"</item>
+ <item msgid="688870735111627832">"Радиодан басқасының барлығы"</item>
+ <item msgid="2850427388488887328">"тек ядро"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Өшірулі"</item>
+ <item msgid="172978079776521897">"Барлық журнал буфері"</item>
+ <item msgid="3873873912383879240">"Радио журналының буферлерінен басқасының барлығы"</item>
+ <item msgid="8489661142527693381">"тек ядро журналының буфері"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Aнимация өшірілген"</item>
<item msgid="6624864048416710414">"Aнимация өлшемі .5x"</item>
diff --git a/packages/SettingsLib/res/values-kk-rKZ/strings.xml b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
index ab5e338..3dcc7eb 100644
--- a/packages/SettingsLib/res/values-kk-rKZ/strings.xml
+++ b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Интерфейсте бар деректер трафигінің мөлшерінің негізінде Wi-Fi роумингін іздеулерге рұқсат ету/тыйым салу"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Журналға тіркеуші буферінің өлшемдері"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Әр журнал буфері үшін журналға тіркеуші өлшемдерін таңдау"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Тіркеуіштің тұрақты жадын тазарту керек пе?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Тұрақты тіркеуішпен бақылауды тоқтатқаннан кейін, құрылғыңыздағы ол сақтаған деректертің бәрін жоюымыз керек."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Тіркеуіш деректерін құрылғыға тұрақты түрде сақтау"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Журнал буферлерін таңдап, құрылғыңызда тұрақты түрде сақтау"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB конфигурациясын таңдау"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB конфигурациясын таңдау"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Жасанды аймақтарға рұқсат беру"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Жасанды аймақтарды пайдалануға рұқсат беру"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Көру төлсипатын тексеруді қосу"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Жаңа Android DHCP клиентінің орнына Lollipop ішіндегі DHCP клиентін пайдалану"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Wi‑Fi қосулы кезде де ұялы деректерді белсенді етіп ұстау (желіні жылдам ауыстыру үшін)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB жөндеулеріне рұқсат берілсін бе?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB жөндеу дамыту мақсаттарына ғана арналған. Оны компьютер және құрылғы арасында дерек көшіру, құрылғыға ескертусіз қолданба орнату және тіркелім деректерін оқу үшін қолданыңыз."</string>
diff --git a/packages/SettingsLib/res/values-km-rKH/arrays.xml b/packages/SettingsLib/res/values-km-rKH/arrays.xml
index 63921bc..17371f6 100644
--- a/packages/SettingsLib/res/values-km-rKH/arrays.xml
+++ b/packages/SettingsLib/res/values-km-rKH/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4M per log buffer"</item>
<item msgid="5431354956856655120">"16M per log buffer"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"បិទ"</item>
+ <item msgid="3054662377365844197">"ទាំងអស់"</item>
+ <item msgid="688870735111627832">"ទាំងអស់ក្រៅពីវិទ្យុ"</item>
+ <item msgid="2850427388488887328">"kernel តែប៉ុណ្ណោះ"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"បិទ"</item>
+ <item msgid="172978079776521897">"អង្គចងចាំកំណត់ហេតុបណ្តោះអាសន្នទាំងអស់"</item>
+ <item msgid="3873873912383879240">"ទាំងអស់ក្រៅពីអង្គចងចាំកំណត់ហេតុវិទ្យុបណ្តោះអាសន្ន"</item>
+ <item msgid="8489661142527693381">"អង្គចងចាំបណ្ដោះអាសន្នកំណត់ហេតុ kernel តែប៉ុណ្ណោះ"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"បិទចលនា"</item>
<item msgid="6624864048416710414">"មាត្រដ្ឋានចលនា .5x"</item>
diff --git a/packages/SettingsLib/res/values-km-rKH/strings.xml b/packages/SettingsLib/res/values-km-rKH/strings.xml
index 30b7f7a..aa0ce24 100644
--- a/packages/SettingsLib/res/values-km-rKH/strings.xml
+++ b/packages/SettingsLib/res/values-km-rKH/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"អនុញ្ញាត/មិនអនុញ្ញាតការវិភាគរ៉ូមវ៉ាយហ្វាយផ្អែកលើចំនួនការបង្ហាញចរាចរណ៍ទិន្នន័យនៅចំណុចប្រទាក់"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"ទំហំ buffer របស់ Logger"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"ជ្រើសទំហំ Logger per log buffer"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"ជម្រះទំហំផ្ទុក logger ដែលប្រើបានយូរឬ?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"នៅពេលដែលយើងឈប់ធ្វើការត្រួតពិនិត្យតទៅទៀតដោយប្រើ logger ដែលប្រើបានយូរ យើងត្រូវបានតម្រូវឲ្យលុបទិន្នន័យ logger ដែលមាននៅលើឧបករណ៍របស់អ្នក"</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"ផ្ទុកទិន្នន័យ logger នៅលើឧបករណ៍ឲ្យបានយូរ"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"ជ្រើសអង្គចងចាំកំណត់ហេតុបណ្តោះអាសន្នដើម្បីផ្ទុកនៅលើឧបករណ៍ឲ្យបានយូរ"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"ជ្រើសការកំណត់រចនាសម្ព័ន្ធយូអេសប៊ី"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"ជ្រើសការកំណត់រចនាសម្ព័ន្ធយូអេសប៊ី"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"ឲ្យក្លែងទីតាំង"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"អនុញ្ញាតទីតាំងក្លែងក្លាយ"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"បើកការត្រួតពិនិត្យគុណលក្ខណៈទិដ្ឋភាព"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"ប្រើម៉ាស៊ីនកូន DHCP ចេញពី Lollipop ជំនួសឲ្យម៉ាស៊ីនកូន Android DHCP ថ្មី។"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"រក្សាទិន្នន័យចល័តឲ្យសកម្មជានិច្ច បើទោះបីជា Wi‑Fi សកម្មក៏ដោយ (សម្រាប់ការប្តូរបណ្តាញដែលមានល្បឿនលឿន)។"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"អនុញ្ញាតការកែកំហុសយូអេសប៊ី?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"ការកែកំហុសយូអេសប៊ីគឺសម្រាប់តែការអភិវឌ្ឍប៉ុណ្ណោះ។ ប្រើវាដើម្បីចម្លងទិន្នន័យរវាងកុំព្យូទ័រ និងឧបករណ៍របស់អ្នក ដំឡើងកម្មវិធីក្នុងឧបករណ៍របស់អ្នកដោយមិនជូនដំណឹង និងអានទិន្នន័យកំណត់ហេតុ។"</string>
diff --git a/packages/SettingsLib/res/values-kn-rIN/arrays.xml b/packages/SettingsLib/res/values-kn-rIN/arrays.xml
index e1e69f7..5f6d699 100644
--- a/packages/SettingsLib/res/values-kn-rIN/arrays.xml
+++ b/packages/SettingsLib/res/values-kn-rIN/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"ಪ್ರತಿ ಲಾಗ್ ಬಫರ್ಗೆ 4M"</item>
<item msgid="5431354956856655120">"ಪ್ರತಿ ಲಾಗ್ ಬಫರ್ಗೆ 16M"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"ಆಫ್"</item>
+ <item msgid="3054662377365844197">"ಎಲ್ಲಾ"</item>
+ <item msgid="688870735111627832">"ಎಲ್ಲಾ ಆದರೆ ರೇಡಿಯೊ"</item>
+ <item msgid="2850427388488887328">"ಕೆರ್ನಲ್ ಮಾತ್ರ"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"ಆಫ್ ಆಗಿದೆ"</item>
+ <item msgid="172978079776521897">"ಎಲ್ಲಾ ಲಾಗ್ ಬಫರ್ಗಳು"</item>
+ <item msgid="3873873912383879240">"ಎಲ್ಲಾ ಆದರೆ ರೇಡಿಯೊ ಲಾಗ್ ಬಫರ್ಗಳು"</item>
+ <item msgid="8489661142527693381">"ಕೆರ್ನಲ್ ಲಾಗ್ ಬಫರ್ ಮಾತ್ರ"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"ಆನಿಮೇಶನ್ ಆಫ್"</item>
<item msgid="6624864048416710414">"ಅನಿಮೇಶನ್ ಮಾಪಕ .5x"</item>
diff --git a/packages/SettingsLib/res/values-kn-rIN/strings.xml b/packages/SettingsLib/res/values-kn-rIN/strings.xml
index 9b322c8..643875f 100644
--- a/packages/SettingsLib/res/values-kn-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-kn-rIN/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"ಇಂಟರ್ಫೇಸ್ನಲ್ಲಿ ಲಭ್ಯವಿರುವ ಡೇಟಾ ಟ್ರಾಫಿಕ್ ಆಧಾರದ ಮೇಲೆ Wi‑Fi ರೋಮ್ ಸ್ಕ್ಯಾನ್ಗಳನ್ನು ಅನುಮತಿಸಿ/ನಿರಾಕರಿಸಿ"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"ಲಾಗರ್ ಬಫರ್ ಗಾತ್ರಗಳು"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"ಪ್ರತಿ ಲಾಗ್ ಬಫರ್ಗೆ ಲಾಗರ್ ಗಾತ್ರಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"ಶಾಶ್ವತವಾಗಿರುವ ಸಂಗ್ರಹಣೆ ಲಾಗರ್ ಅನ್ನು ತೆರವುಗೊಳಿಸುವುದೇ?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"ನಾವು ನಿರಂತರವಾಗಿ ಲಾಗರ್ ಮೂಲಕ ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡದಿರುವಾಗ, ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಸಂಗ್ರಹವಾಗಿರುವಂತಹ ಲಾಗರ್ ಡೇಟಾವನ್ನು ಅಳಿಸುವುದು ನಮಗೆ ಅಗತ್ಯವಿರುತ್ತದೆ."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"ಲಾಗರ್ ಡೇಟಾವನ್ನು ಸಾಧನದಲ್ಲಿ ನಿರಂತರವಾಗಿ ಸಂಗ್ರಹಿಸಿ"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"ಸಾಧನದಲ್ಲಿ ನಿರಂತರವಾಗಿ ಸಂಗ್ರಹಿಸಲು ಲಾಗ್ ಬಫರ್ಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB ಕಾನ್ಫಿಗರೇಶನ್ ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB ಕಾನ್ಫಿಗರೇಶನ್ ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"ಅಣಕು ಸ್ಥಾನಗಳನ್ನು ಅನುಮತಿಸು"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"ಅಣಕು ಸ್ಥಾನಗಳನ್ನು ಅನುಮತಿಸು"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"ವೀಕ್ಷಣೆ ಆಟ್ರಿಬ್ಯೂಟ್ ಪರಿಶೀಲನೆ"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"ಹೊಸ Android DHCP ಕ್ಲೈಂಟ್ ಬದಲಾಗಿ Lollipop ನಿಂದ DHCP ಕ್ಲೈಂಟ್ ಬಳಸಿ."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"ವೈ-ಫೈ ಸಕ್ರಿಯವಾಗಿರುವಾಗಲೂ, ಯಾವಾಗಲೂ ಮೊಬೈಲ್ ಡೇಟಾ ಸಕ್ರಿಯವಾಗಿರಿಸಿ (ವೇಗವಾಗಿ ನೆಟ್ವರ್ಕ್ ಬದಲಾಯಿಸಲು)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB ಡೀಬಗ್ ಮಾಡುವಿಕೆಯನ್ನು ಅನುಮತಿಸುವುದೇ?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB ಡೀಬಗ್ ಮಾಡುವಿಕೆಯು ಅಭಿವೃದ್ಧಿ ಉದ್ದೇಶಗಳಿಗೆ ಮಾತ್ರ ಆಗಿದೆ. ನಿಮ್ಮ ಕಂಪ್ಯೂಟರ್ ಮತ್ತು ನಿಮ್ಮ ಸಾಧನದ ನಡುವೆ ಡೇಟಾವನ್ನು ನಕಲಿಸಲು, ಅಧಿಸೂಚನೆ ಇಲ್ಲದೆ ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಸ್ಥಾಪಿಸಲು ಮತ್ತು ಲಾಗ್ ಡೇಟಾ ಓದಲು ಅದನ್ನು ಬಳಸಿ."</string>
@@ -337,5 +340,5 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"ಸ್ವಲ್ಪ ದೊಡ್ಡ"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ದೊಡ್ಡ"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"ಕಸ್ಟಮ್ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"ಸಹಾಯ & ಪ್ರತಿಕ್ರಿಯೆ"</string>
+ <string name="help_feedback_label" msgid="6815040660801785649">"ಸಹಾಯ ಮತ್ತು ಪ್ರತಿಕ್ರಿಯೆ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ko/arrays.xml b/packages/SettingsLib/res/values-ko/arrays.xml
index 3e8867e..596f93e 100644
--- a/packages/SettingsLib/res/values-ko/arrays.xml
+++ b/packages/SettingsLib/res/values-ko/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"로그 버퍼당 4M"</item>
<item msgid="5431354956856655120">"로그 버퍼당 16M"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"사용 안함"</item>
+ <item msgid="3054662377365844197">"전체"</item>
+ <item msgid="688870735111627832">"라디오 외 모두"</item>
+ <item msgid="2850427388488887328">"커널만"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"사용 안함"</item>
+ <item msgid="172978079776521897">"모든 로그 버퍼"</item>
+ <item msgid="3873873912383879240">"라디오 로그 버퍼를 제외한 모든 버퍼"</item>
+ <item msgid="8489661142527693381">"커널 로그 버퍼만"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"애니메이션 사용 안함"</item>
<item msgid="6624864048416710414">"애니메이션 배율 .5x"</item>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index dace41b..4617681 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"인터페이스에 표시되는 데이터 트래픽의 양을 기반으로 Wi-Fi 로밍 스캔을 허용하거나 허용하지 않습니다."</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"로거 버퍼 크기"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"로그 버퍼당 로거 크기 선택"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"로거 영구 저장소를 삭제하시겠습니까?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"영구 로거로 더 이상 모니터링하지 않는 경우 기기에 있는 로거 데이터를 삭제해야 합니다."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"로거 데이터를 기기에 영구 저장"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"기기에 영구 저장할 로그 버퍼 선택"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB 설정 선택"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB 설정 선택"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"모의 위치 허용"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"모의 위치 허용"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"보기 속성 검사 사용"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"새로운 Android DHCP 클라이언트 대신 Lollipop의 DHCP 클라이언트 사용"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Wi‑Fi가 활성화되어 있을 때에도 빠른 네트워크 전환을 위하여 항상 모바일 데이터를 활성 상태로 유지합니다."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB 디버깅을 허용하시겠습니까?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB 디버깅은 개발용으로만 설계되었습니다. 이 기능을 사용하면 컴퓨터와 기기 간에 데이터를 복사하고 알림 없이 기기에 앱을 설치하며 로그 데이터를 읽을 수 있습니다."</string>
@@ -337,5 +340,5 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"더 크게"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"가장 크게"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"맞춤(<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"도움말 및 의견"</string>
+ <string name="help_feedback_label" msgid="6815040660801785649">"고객센터"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ky-rKG/arrays.xml b/packages/SettingsLib/res/values-ky-rKG/arrays.xml
index 81beebe..33a0b5d 100644
--- a/packages/SettingsLib/res/values-ky-rKG/arrays.xml
+++ b/packages/SettingsLib/res/values-ky-rKG/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"Буфер: 4М ашпашы керек"</item>
<item msgid="5431354956856655120">"Буфер: 16М ашпашы керек"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Өчүк"</item>
+ <item msgid="3054662377365844197">"Бардыгы"</item>
+ <item msgid="688870735111627832">"Радиодон башка"</item>
+ <item msgid="2850427388488887328">"өзөк гана"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Өчүк"</item>
+ <item msgid="172978079776521897">"Бардык таржымал буферлери"</item>
+ <item msgid="3873873912383879240">"Радио таржымал буферлеринен башкаларынын баары"</item>
+ <item msgid="8489661142527693381">"өзөктүк журнал буфери гана"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Анимацияны өчүрүү"</item>
<item msgid="6624864048416710414">"Анимация масштабы .5x"</item>
diff --git a/packages/SettingsLib/res/values-ky-rKG/strings.xml b/packages/SettingsLib/res/values-ky-rKG/strings.xml
index 5bc08ac..96c1ec0 100644
--- a/packages/SettingsLib/res/values-ky-rKG/strings.xml
+++ b/packages/SettingsLib/res/values-ky-rKG/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Интерфейстеги дайындар трафигинин көлөмүнө жараша Wi-Fi Роуминг скандоо мүмкүнчүлүгүн иштетүү/өчүрүү"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Каттагыч буферлеринин өлчөмдөрү"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Каттоо буфери үчүн Каттагычтын көлөмүн тандаңыз"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Таржымалдын туруктуу диски тазалансынбы?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Туруктуу таржымалга көз салууну токтотсок, анын түзмөктө сакталган дайындарын жок кылууга аргасыз болобуз."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Таржымалдагы дайындар түзмөккө сакталсын"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Түзмөккө туруктуу сактоо үчүн таржымал буферлерин тандаңыз"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB конфигурациясын тандоо"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB конфигурациясын тандоо"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Жасалма жайгашкан жерди көрсөтүүгө уруксат берилсин"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Жасалма жайгашкан жерди көрсөтүүгө уруксат берилсин"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Аттрибут текшерүүсүнүн көрүнүшүн иштетүү"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Жаңы Android DHCP кардарынын ордуна Lollipop\'тон DHCP кардарын колдонуңуз."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Wi-Fi иштеп турганда да дайындар мобилдик тармак аркылуу өткөрүлө берсин (тармактар ортосунда тезирээк которулуу үчүн)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB аркылуу жөндөөгө уруксат берилсинби?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB-жөндөө - өндүрүү максатында гана түзүлгөн. Аны компүтериңиз менен түзмөгүңүздүн ортосунда берилиштерди алмашуу, түзмөгүңүзгө колдонмолорду эскертүүсүз орнотуу жана лог берилиштерин окуу үчүн колдонсоңуз болот."</string>
diff --git a/packages/SettingsLib/res/values-lo-rLA/arrays.xml b/packages/SettingsLib/res/values-lo-rLA/arrays.xml
index 312ecc0..7a6b77a 100644
--- a/packages/SettingsLib/res/values-lo-rLA/arrays.xml
+++ b/packages/SettingsLib/res/values-lo-rLA/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"ບັບເຟີ 4M ຕໍ່ບັນທຶກ"</item>
<item msgid="5431354956856655120">"ບັບເຟີ 16M ຕໍ່ບັນທຶກ"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"ປິດ"</item>
+ <item msgid="3054662377365844197">"ທັງໝົດ"</item>
+ <item msgid="688870735111627832">"ທັງໝົດຍົກເວັ້ນວິທະຍຸ"</item>
+ <item msgid="2850427388488887328">"ເຄີນເນວເທົ່ານັ້ນ"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"ປິດ"</item>
+ <item msgid="172978079776521897">"ບັບເຟີບັນທຶກທັງໝົດ"</item>
+ <item msgid="3873873912383879240">"ທັງໝົດຍົກເວັ້ນບັບເຟີບັນທຶກວິທະຍຸ"</item>
+ <item msgid="8489661142527693381">"ບັບເຟີບັນທຶກເຄີນເນວເທົ່ານັ້ນ"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"ປິດອະນິເມຊັນ"</item>
<item msgid="6624864048416710414">"ຂະໜາດອະນິເມຊັນ .5x"</item>
diff --git a/packages/SettingsLib/res/values-lo-rLA/strings.xml b/packages/SettingsLib/res/values-lo-rLA/strings.xml
index 6e13c9f..24f0c16 100644
--- a/packages/SettingsLib/res/values-lo-rLA/strings.xml
+++ b/packages/SettingsLib/res/values-lo-rLA/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"ອະນຸຍາດ/ບໍ່ອະນຸຍາດການສະແກນການໂຣມ Wi-Fi ອີງຕາມຈຳນວນຂໍ້ມູນທີ່ເກີດຂຶ້ນໃນລະດັບສ່ວນຕິດຕໍ່"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"ຂະໜາດບັບເຟີໂຕລັອກ"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"ເລືອກຂະໜາດລັອກຕໍ່ບັບເຟີ"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"ລຶບລ້າງບ່ອນຈັດເກັບຖາວອນຂອງຕົວບັນທຶກບໍ່?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"ເມື່ອພວກເຮົາບໍ່ກວດສອບຕົວບັນທຶກຖາວອນ, ພວກເຮົາຈະຕ້ອງລຶບຂໍ້ມູນຕົວບັນທຶກໃນອຸປະກອນຂອງທ່ານອອກ."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"ຈັດເກັບຂໍ້ມູນຕົວບັນທຶກໄວ້ຖາວອນໃນອຸປະກອນ"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"ເລືອກບັບເຟີບັນທຶກເພື່ອຈັດເກັບຖາວອນໃນອຸປະກອນ"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"ເລືອກການຕັ້ງຄ່າ USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"ເລືອກການຕັ້ງຄ່າ USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"ອະນຸຍາດໃຫ້ຈຳລອງຕຳແໜ່ງ"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"ອະນຸຍາດໃຫ້ຈຳລອງຕຳແໜ່ງ"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"ເປີດນຳໃຊ້ການກວດສອບຄຸນສົມບັດມຸມມອງ"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"ໃຊ້ລູກຄ້າ DHCP ຈາກ Lollipop ແທນລູກຄ້າ Android DHCP ໃໝ່."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"ໃຫ້ຂໍ້ມູນມືຖືເປີດຢູ່ສະເໝີ, ແມ້ແຕ່ເມື່ອ Wi‑Fi ເປີດຢູ່ (ສຳລັບການສະຫຼັບເຄືອຂ່າຍໄວ)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"ອະນຸຍາດໃຫ້ດີບັ໊ກຜ່ານ USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"ການດີບັ໊ກຜ່ານ USB ແມ່ນມີຈຸດປະສົງເພື່ອການພັດທະນາເທົ່ານັ້ນ. ມັນສາມາດໃຊ້ເພື່ອສຳເນົາຂໍ້ມູນລະຫວ່າງຄອມພິວເຕີ ແລະອຸປະກອນຂອງທ່ານ, ຕິດຕັ້ງແອັບຯໂດຍບໍ່ຜ່ານການແຈ້ງເຕືອນ ແລະອ່ານຂໍ້ມູນການບັນທຶກ."</string>
diff --git a/packages/SettingsLib/res/values-lt/arrays.xml b/packages/SettingsLib/res/values-lt/arrays.xml
index 95cc763..49908ad 100644
--- a/packages/SettingsLib/res/values-lt/arrays.xml
+++ b/packages/SettingsLib/res/values-lt/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 MB žurnalo buferis"</item>
<item msgid="5431354956856655120">"16 MB žurnalo buferis"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Išjungta"</item>
+ <item msgid="3054662377365844197">"Viskas"</item>
+ <item msgid="688870735111627832">"Viskas, išsk. rad. r."</item>
+ <item msgid="2850427388488887328">"tik branduolys"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Išjungta"</item>
+ <item msgid="172978079776521897">"Visi žurnalų buferiai"</item>
+ <item msgid="3873873912383879240">"Visi, išskyrus radijo ryšio žurnalų buferius"</item>
+ <item msgid="8489661142527693381">"tik branduolio žurnalo buferis"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animacija išjungta"</item>
<item msgid="6624864048416710414">"Animacijos mastelis 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index d409366..4b8890d 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Leisti / neleisti „Wi‑Fi“ tarptinklinio ryšio nuskaitymo, atsižvelgiant į sąsajos duomenų srauto kiekį"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Registruotuvo buferio dydžiai"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Pasir. registr. dydž. žurn. bufer."</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Išvalyti nuolatinę registruotuvo saugyklą?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Kai nebestebime naudodami nuolatinį registruotuvą, turime ištrinti įrenginyje esančius registruotuvo duomenis."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Nuol. saug. regist. duom. įreng."</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Pasirinkite žurnalų buferius, kurie bus nuolat saugomi įrenginyje"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Pasirinkite USB konfigūraciją"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Pasirinkite USB konfigūraciją"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Leisti imituoti vietas"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Leisti imituoti vietas"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Įgalinti peržiūros atributų tikrinimą"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"DHCP kliento programos iš „Lollipop“ versijos naudojimas vietoje naujos „Android“ DHCP kliento programos."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Visada suaktyvinti mobiliojo ryšio duomenis, net kai aktyvus „Wi‑Fi“ ryšys (kad būtų galima greitai perjungti tinklą)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Leisti USB perkrovimą?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB derinimas skirtas naudoti tik kūrimo tikslais. Jis gali būti naudojamas norint kopijuoti duomenis iš kompiuterio į įrenginį ir atvirkščiai, įdiegti programas įrenginyje be pranešimo ir skaityti žurnalo duomenis."</string>
diff --git a/packages/SettingsLib/res/values-lv/arrays.xml b/packages/SettingsLib/res/values-lv/arrays.xml
index 03aff8c..801de5d 100644
--- a/packages/SettingsLib/res/values-lv/arrays.xml
+++ b/packages/SettingsLib/res/values-lv/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 MB vienam žurnāla buferim"</item>
<item msgid="5431354956856655120">"16 MB vienam žurnāla buferim"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Izslēgts"</item>
+ <item msgid="3054662377365844197">"Visi"</item>
+ <item msgid="688870735111627832">"Visi, izņemot radio"</item>
+ <item msgid="2850427388488887328">"tikai kodolā"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Izslēgts"</item>
+ <item msgid="172978079776521897">"Visi reģistra buferi"</item>
+ <item msgid="3873873912383879240">"Visi, izņemot radio reģistra buferus"</item>
+ <item msgid="8489661142527693381">"tikai kodola žurnāla buferī"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animācija ir izslēgta"</item>
<item msgid="6624864048416710414">"Animācijas mērogs: 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 7dfd9ac..4d76adc 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Atļaujiet/neatļaujiet Wi‑Fi meklēšanu, pamatojoties uz saskarnē saņemto datplūsmas apjomu."</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Reģistrētāja buferu lielumi"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Atlasīt reģistrētāja bufera liel."</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Vai notīrīt reģistrētāja pastāvīgo krātuvi?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Kad uzraudzībai vairs neizmantojam pastāvīgo reģistrētāju, mums ir jāizdzēš reģistrētāja dati, kas tiek glabāti jūsu ierīcē."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Pastāvīgi glabāt ierīcē reģistrētāja datus"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Atlasiet reģistra buferus, ko pastāvīgi glabāt ierīcē"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Atlasīt USB konfigurāciju"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Atlasīt USB konfigurāciju"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Atļaut neīstas vietas"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Atļaut neīstas vietas"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Iespējot atribūtu pārbaudi"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Lietot DHCP klientu no operētājsistēmas Lollipop, nevis jauno Android DHCP klientu."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Mobilo datu savienojums būs vienmēr aktīvs, pat ja būs aktīvs Wi-Fi savienojums (ātrai ierīces pārslēgšanai uz citu tīklu)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Vai atļaut USB atkļūdošanu?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB atkļūdošana ir paredzēta tikai ar izstrādi saistītām darbībām. Izmantojiet to datu kopēšanai no datora uz ierīci un pretēji, lietotņu instalēšanai ierīcē bez paziņojumiem un žurnāla datu lasīšanai."</string>
diff --git a/packages/SettingsLib/res/values-mk-rMK/arrays.xml b/packages/SettingsLib/res/values-mk-rMK/arrays.xml
index 588c21a..a72bcd3 100644
--- a/packages/SettingsLib/res/values-mk-rMK/arrays.xml
+++ b/packages/SettingsLib/res/values-mk-rMK/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 M/меѓумеморија"</item>
<item msgid="5431354956856655120">"16 M/меѓумеморија"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Исклучено"</item>
+ <item msgid="3054662377365844197">"Сите"</item>
+ <item msgid="688870735111627832">"Сите освен радио"</item>
+ <item msgid="2850427388488887328">"само јадро"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Исклучено"</item>
+ <item msgid="172978079776521897">"Привремена меморија на целата евиденција"</item>
+ <item msgid="3873873912383879240">"Привремена мем. на цела евиденција освен за радио"</item>
+ <item msgid="8489661142527693381">"само привремена меморија за евиденција на јадро"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Без анимација"</item>
<item msgid="6624864048416710414">"Опсег на анимација 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-mk-rMK/strings.xml b/packages/SettingsLib/res/values-mk-rMK/strings.xml
index 7d6e43b..953360a 100644
--- a/packages/SettingsLib/res/values-mk-rMK/strings.xml
+++ b/packages/SettingsLib/res/values-mk-rMK/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Дозволи/Забрани Wi‑Fi скенирање во роаминг според количината на постоечкиот податочен сообраќај на интерфејсот."</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Величини на меѓумеморија на забележувач"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Величина/меѓумеморија на дневник"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Да се избрише постојаната меморија на дневникот?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Кога веќе не го следиме постојаниот дневник, мора да ги избришеме податоците на дневникот што се наоѓаат на вашиот уред."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Зачувувај податоци на дневникот"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Изберете привремена меморија на евиденција што ќе се користи постојано на уредот"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Изберете конфигурација за УСБ"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Изберете конфигурација за УСБ"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Овозможи лажни локации"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Овозможи лажни локации"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Овозможете проверка на атрибутот на приказот"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Користете го клиентот на DHCP од Lollipop наместо новиот клиент на DHCP на Android."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Секогаш држи го активен мобилниот интернет, дури и при активно Wi-Fi (за брзо префрлување мрежа)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Овозможи отстранување грешки на УСБ?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Отстранувањето грешки на УСБ е наменето само за целите на развој. Користете го за копирање податоци меѓу вашиот компјутер и вашиот уред, за инсталирање апликации на вашиот уред без известување и за читање евиденција на податоци."</string>
diff --git a/packages/SettingsLib/res/values-ml-rIN/arrays.xml b/packages/SettingsLib/res/values-ml-rIN/arrays.xml
index f04cc65..ac6841e 100644
--- a/packages/SettingsLib/res/values-ml-rIN/arrays.xml
+++ b/packages/SettingsLib/res/values-ml-rIN/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"ഓരോ ലോഗ് ബഫറിനും 4M"</item>
<item msgid="5431354956856655120">"ഓരോ ലോഗ് ബഫറിനും 16M"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"ഓഫ്"</item>
+ <item msgid="3054662377365844197">"എല്ലാം"</item>
+ <item msgid="688870735111627832">"റേഡിയോ ഒഴികെയുള്ള എല്ലാം"</item>
+ <item msgid="2850427388488887328">"കേർനൽ മാത്രം"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"ഓഫ്"</item>
+ <item msgid="172978079776521897">"എല്ലാ ലോഗ് ബഫറുകളും"</item>
+ <item msgid="3873873912383879240">"റേഡിയോ ലോഗ് ബഫറുകൾ ഒഴികെയുള്ള എല്ലാം"</item>
+ <item msgid="8489661142527693381">"കേർനൽ ലോഗ് ബഫർ മാത്രം"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"ആനിമേഷൻ ഓഫുചെയ്യുക"</item>
<item msgid="6624864048416710414">"ആനിമേഷൻ സ്കെയിൽ .5x"</item>
diff --git a/packages/SettingsLib/res/values-ml-rIN/strings.xml b/packages/SettingsLib/res/values-ml-rIN/strings.xml
index 17b42e9..92c0448 100644
--- a/packages/SettingsLib/res/values-ml-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ml-rIN/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"ഇന്റർഫേസിലെ ഡാറ്റ ട്രാഫിക്ക് സാന്നിദ്ധ്യത്തിന്റെ കണക്ക് അടിസ്ഥാനമാക്കി വൈഫൈ റോം സ്കാനുകൾ അനുവദിക്കുക/അനുവദിക്കാതിരിക്കുക"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"ലോഗർ ബഫർ വലുപ്പം"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"ഓരോ ലോഗ് ബഫറിനും വലുപ്പം തിരഞ്ഞെടുക്കൂ"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"ലോഗർ സ്ഥിര സ്റ്റോറേജ് മായ്ക്കണോ?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"സ്ഥിര ലോഗർ ഉപയോഗിച്ച് ഞങ്ങൾ തുടർന്നങ്ങോട്ട് നിരീക്ഷിക്കാത്ത സാഹചര്യത്തിൽ, നിങ്ങളുടെ ഉപകരണത്തിൽ നിലവിലുള്ള ലോഗർ വിവരങ്ങൾ ഞങ്ങൾ മായ്ക്കേണ്ടതുണ്ട്."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"ഉപകരണത്തിൽ സ്ഥിരമായി ലോഗർ വിവരങ്ങൾ സംഭരിക്കുക"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"ഉപകരണത്തിൽ സ്ഥിരമായി സംഭരിക്കുന്നതിനുള്ള ലോഗ് ബഫറുകൾ തിരഞ്ഞെടുക്കുക"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB കോൺഫിഗറേഷൻ തിരഞ്ഞെടുക്കൂ"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB കോൺഫിഗറേഷൻ തിരഞ്ഞെടുക്കൂ"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"വ്യാജ ലൊക്കേഷനുകൾ അനുവദിക്കുക"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"വ്യാജ ലൊക്കേഷനുകൾ അനുവദിക്കുക"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"ആട്രിബ്യൂട്ട് പരിശോധന കാണൽ സജീവമാക്കൂ"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"പുതിയ Android DHCP ക്ലയന്റിനുപകരം Lollipop-ൽ നിന്ന് DHCP ക്ലയന്റ് ഉപയോഗിക്കുക."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"വൈഫൈ സജീവമാണെങ്കിലും, മൊബൈൽ ഡാറ്റ സജീവമായി നിർത്തുക (വേഗത്തിൽ നെറ്റ്വർക്ക് മാറുന്നതിനായി)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB ഡീബഗ്ഗുചെയ്യാൻ അനുവദിക്കണോ?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB ഡീബഗ്ഗിംഗ് വികസന ആവശ്യകതകൾക്ക് മാത്രമുള്ളതാണ്. നിങ്ങളുടെ കമ്പ്യൂട്ടറിനും ഉപകരണത്തിനുമിടയിൽ ഡാറ്റ പകർത്തുന്നതിനും അറിയിപ്പില്ലാതെ തന്നെ നിങ്ങളുടെ ഉപകരണത്തിൽ അപ്ലിക്കേഷനുകൾ ഇൻസ്റ്റാളുചെയ്യുന്നതിനും ലോഗ് ഡാറ്റ റീഡുചെയ്യുന്നതിനും ഇത് ഉപയോഗിക്കുക."</string>
diff --git a/packages/SettingsLib/res/values-mn-rMN/arrays.xml b/packages/SettingsLib/res/values-mn-rMN/arrays.xml
index 79b9fea..e812043 100644
--- a/packages/SettingsLib/res/values-mn-rMN/arrays.xml
+++ b/packages/SettingsLib/res/values-mn-rMN/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"лог буфер бүрт 4M"</item>
<item msgid="5431354956856655120">"лог буфер бүрт 16M"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Идэвхгүй"</item>
+ <item msgid="3054662377365844197">"Бүгд"</item>
+ <item msgid="688870735111627832">"Радиогоос бусад бүх"</item>
+ <item msgid="2850427388488887328">"зөвхөн төвд"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Идэвхгүй"</item>
+ <item msgid="172978079776521897">"Бүх логийн хамгаалалт"</item>
+ <item msgid="3873873912383879240">"Радиогоос бусад бүх логийн хамгаалалт"</item>
+ <item msgid="8489661142527693381">"зөвхөн үйлдлийн системийн төв логийн хамгаалалтад"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Дүрс амилуулалт идэвхгүй"</item>
<item msgid="6624864048416710414">"Дүрс амилуулах далайц .5x"</item>
diff --git a/packages/SettingsLib/res/values-mn-rMN/strings.xml b/packages/SettingsLib/res/values-mn-rMN/strings.xml
index 359c041..4c98c04 100644
--- a/packages/SettingsLib/res/values-mn-rMN/strings.xml
+++ b/packages/SettingsLib/res/values-mn-rMN/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Интерфэйс дээрх дата трафикын хэмжээнээс хамааран Wi‑Fi Роум Скан-г зөвшөөрөх/үл зөвшөөрөх"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Логгерын буферын хэмжээ"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Лог буфер бүрт ногдох логгерын хэмжээг сонгоно уу"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Нэвтрэгчийн тогтмол санг устгах уу?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Бид байнгын логоор хянаагүй үед таны төхөөрөмжтэй холбоотой нэвтрэгч өгөгдлийг устгах шаардлагатай."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Төхөөрөмжид тогтмол нэвтрэгчийн өгөгдлийн сан"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Төхөөрөмжид тогтмол хадгалах логийн хамгаалалтыг сонгох"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB Тохируулга сонгох"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB Тохируулга сонгох"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Хуурамч байршлыг зөвшөөрөх"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Хуурамч байршлыг зөвшөөрөх"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Харах тохируулгын шалгалтыг идэвхжүүлэх"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Шинэ Андройд DHCP харилцагчийн оронд Lollipop-ийн DHCP харилцагчийг хэрэглэ."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Wi‑Fi идэвхтэй байхад ч гэсэн гар утасны датаг идэвхтэй байлгадаг (сүлжээг түргэн солихын тулд)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB дебаг хийхийг зөвшөөрөх үү?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB дебаг нь зөвхөн хөгжүүлэлтийн зорилготой. Үүнийг өөрийн компьютер болон төхөөрөмжийн хооронд өгөгдөл хуулах, өөрийн төхөөрөмж дээр мэдэгдэлгүйгээр аппликешн суулгах, лог датаг унших зэрэгт ашиглаж болно."</string>
diff --git a/packages/SettingsLib/res/values-mr-rIN/arrays.xml b/packages/SettingsLib/res/values-mr-rIN/arrays.xml
index 2f07389..5de2f49 100644
--- a/packages/SettingsLib/res/values-mr-rIN/arrays.xml
+++ b/packages/SettingsLib/res/values-mr-rIN/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"प्रति लॉग बफर 4M"</item>
<item msgid="5431354956856655120">"प्रति लॉग बफर 16M"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"बंद"</item>
+ <item msgid="3054662377365844197">"सर्व"</item>
+ <item msgid="688870735111627832">"सर्व परंतु रेडिओ"</item>
+ <item msgid="2850427388488887328">"फक्त कर्नेल"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"बंद"</item>
+ <item msgid="172978079776521897">"सर्व लॉग बफर"</item>
+ <item msgid="3873873912383879240">"सर्व परंतु रेडिओ लॉग बफर"</item>
+ <item msgid="8489661142527693381">"फक्त कर्नेल लॉग बफर"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"अॅनिमेशन बंद"</item>
<item msgid="6624864048416710414">"अॅनिमेशन स्केल .5x"</item>
diff --git a/packages/SettingsLib/res/values-mr-rIN/strings.xml b/packages/SettingsLib/res/values-mr-rIN/strings.xml
index 1d5dc19..e3a7cc4 100644
--- a/packages/SettingsLib/res/values-mr-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-mr-rIN/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"वाय-फाय रोम स्कॅनला इंटरफेसवर उपस्थित असलेल्या रहदारी डेटाच्या प्रमाणावर आधारित अनुमती द्या/अनुमती देऊ नका"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"लॉगर बफर आकार"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"प्रति लॉग बफर लॉगर आकार निवडा"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"लॉगरवर सतत असणारा संचय साफ करायचा?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"सातत्याच्या लॉगरसह आम्ही परीक्षण करीत नसतो तेव्हा, आम्हाला आपल्या डिव्हाइसवर असणारा लॉगर डेटा मिटविणे आवश्यक असते."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"डिव्हाइसवर सातत्याने लॉगर डेटा संचयित करा"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"डिव्हाइसवर सातत्याने संचयित करण्यासाठी लॉग बफर निवडा"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB कॉन्फिगरेशन निवडा"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB कॉन्फिगरेशन निवडा"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"बनावट स्थानांना अनुमती द्या"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"बनावट स्थानांना अनुमती द्या"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"दृश्य विशेषता तपासणी सक्षम करा"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"नवीन Android DHCP क्लायंट ऐवजी Lollipop वरून DHCP क्लायंटचा वापर करा."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"जरी वाय-फाय सक्रिय असले तरीही, नेहमी मोबाईल डेटा सक्रिय ठेवा (जलद नेटवर्क स्विच करण्यासाठी)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB डीबग करण्यास अनुमती द्यायची?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB डीबग करण्याचा हेतू फक्त विकासाच्या उद्देशांसाठी आहे. याचा वापर आपला संगणक आणि आपले डिव्हाइस यांच्या दरम्यान डेटा कॉपी करण्यासाठी करा, सूचनेशिवाय आपल्या डिव्हाइसवर अॅप्स स्थापित करा आणि लॉग डेटा वाचा."</string>
diff --git a/packages/SettingsLib/res/values-ms-rMY/arrays.xml b/packages/SettingsLib/res/values-ms-rMY/arrays.xml
index 49f0f28..d6ea50c 100644
--- a/packages/SettingsLib/res/values-ms-rMY/arrays.xml
+++ b/packages/SettingsLib/res/values-ms-rMY/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4M per penimbal log"</item>
<item msgid="5431354956856655120">"16M per penimbal log"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Mati"</item>
+ <item msgid="3054662377365844197">"Semua"</item>
+ <item msgid="688870735111627832">"Sma kcli radio"</item>
+ <item msgid="2850427388488887328">"inti sahaja"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Mati"</item>
+ <item msgid="172978079776521897">"Semua penimbal log"</item>
+ <item msgid="3873873912383879240">"Semua kecuali penimbal log radio"</item>
+ <item msgid="8489661142527693381">"penimbal log inti sahaja"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animasi dimatikan"</item>
<item msgid="6624864048416710414">"Skala animasi .5x"</item>
diff --git a/packages/SettingsLib/res/values-ms-rMY/strings.xml b/packages/SettingsLib/res/values-ms-rMY/strings.xml
index 8809f71..a1caa2a 100644
--- a/packages/SettingsLib/res/values-ms-rMY/strings.xml
+++ b/packages/SettingsLib/res/values-ms-rMY/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Benarkan/Jangan benarkan Imbasan Perayauan Wi-Fi berdasarkan jumlah trafik data yang ada pada antara muka"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Saiz penimbal pengelog"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Pilih saiz Pengelog bagi setiap penimbal log"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Kosongkan storan gigih pengelog?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Apabila kami tidak lagi memantau menggunakan pengelog gigih, kami dikehendaki untuk memadamkan data pengelog yang menghuni peranti anda."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Smpn data pengelog secara gigih pd prnti"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Pilih penimbal log untuk menyimpan secara gigih pada peranti"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Pilih Konfigurasi USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Pilih Konfigurasi USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Benarkan lokasi olokan"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Benarkan lokasi olokan"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Dayakan pemeriksaan atribut paparan"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Gunakan pelanggan DHCP daripada Lollipop bukannya pelanggan DHCP Android baharu."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Pastikan data mudah alih sentiasa aktif, walaupun Wi-Fi aktif (untuk penukaran rangkaian yang pantas)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Benarkan penyahpepijatan USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Penyahpepijatan USB adalah dimaksudkan untuk tujuan pembangunan sahaja. Gunakannya untuk menyalin data antara komputer dan peranti anda, memasang aplikasi pada peranti anda tanpa pemberitahuan, dan membaca data log."</string>
diff --git a/packages/SettingsLib/res/values-my-rMM/arrays.xml b/packages/SettingsLib/res/values-my-rMM/arrays.xml
index ad3a59f..7ba8e94 100644
--- a/packages/SettingsLib/res/values-my-rMM/arrays.xml
+++ b/packages/SettingsLib/res/values-my-rMM/arrays.xml
@@ -49,12 +49,12 @@
<item msgid="1805837518286731242">"နှေးကွေးသောဆက်သွယ်မှုကို ယာယီရှောင်ရှားထားသည်"</item>
</string-array>
<string-array name="hdcp_checking_titles">
- <item msgid="441827799230089869">"မည်သည့်အခါမှ မစစ်ဆေးပါနှင့်"</item>
+ <item msgid="441827799230089869">"ဘယ်တော့မှ မစစ်ဆေးပါနှင့်"</item>
<item msgid="6042769699089883931">"DRMအကြောင်းအရာကိုသာ စစ်ဆေးမည်"</item>
<item msgid="9174900380056846820">"အမြဲစစ်ဆေးရန်"</item>
</string-array>
<string-array name="hdcp_checking_summaries">
- <item msgid="505558545611516707">"HDCP checkingအားမည်သည့်အခါမျှမသုံးပါနှင့်"</item>
+ <item msgid="505558545611516707">"HDCP checking အား ဘယ်တော့မှ မသုံးပါနှင့်"</item>
<item msgid="3878793616631049349">"DRMအကြောင်းအရာအတွက် HDCPစစ်ဆေးခြင်းကိုသုံးမည်"</item>
<item msgid="45075631231212732">"HDCP checkingအားအမြဲသုံးပါ"</item>
</string-array>
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"မှတ်တမ်းယာယီကြားခံနယ်တစ်ခုလျှင် 4M"</item>
<item msgid="5431354956856655120">"မှတ်တမ်းယာယီကြားခံနယ်တစ်ခုလျှင် 16M"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"ပိတ်ပါ"</item>
+ <item msgid="3054662377365844197">"အားလုံး"</item>
+ <item msgid="688870735111627832">"ရေဒီယိုမှလွဲ၍ အားလုံး"</item>
+ <item msgid="2850427388488887328">"ကာနယ်သာ"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"ပိတ်ပါ"</item>
+ <item msgid="172978079776521897">"မှတ်တမ်းသိမ်းဆည်းရန် လျာထားချက်များ အားလုံး"</item>
+ <item msgid="3873873912383879240">"ရေဒီယို မှတ်တမ်းသိမ်းဆည်းရန်လျာထားချက်မှလွဲ၍ အားလုံး"</item>
+ <item msgid="8489661142527693381">"ကာနယ်မှတ်တမ်းသိမ်းဆည်းရန် လျာထားချက်သာ"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"လှုပ်ရှားသက်ဝင်မှုပုံများကိုပိတ်ပါ"</item>
<item msgid="6624864048416710414">"လှုပ်ရှားသက်ဝင်မှုပုံ စကေး ၅ဆ"</item>
diff --git a/packages/SettingsLib/res/values-my-rMM/strings.xml b/packages/SettingsLib/res/values-my-rMM/strings.xml
index d035e13..6a1c331 100644
--- a/packages/SettingsLib/res/values-my-rMM/strings.xml
+++ b/packages/SettingsLib/res/values-my-rMM/strings.xml
@@ -71,7 +71,7 @@
<string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ထည့်သွင်းရန်အသုံးပြုသည်"</string>
<string name="bluetooth_pairing_accept" msgid="6163520056536604875">"အတူတွဲပါ"</string>
<string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ချိတ်တွဲရန်"</string>
- <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"မလုပ်တော့ပါ"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"မလုပ်တော့"</string>
<string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"ချိတ်တွဲမှုက ချိတ်ဆက်ထားလျှင် သင်၏ အဆက်အသွယ်များ နှင့် ခေါ်ဆိုမှု မှတ်တမ်းကို ရယူခွင့် ပြုသည်။"</string>
<string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>နှင့် တွဲချိတ်မရပါ"</string>
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ပင်နံပါတ် သို့မဟုတ် ဖြတ်သန်းခွင့်ကီးမမှန်ကန်သောကြောင့်<xliff:g id="DEVICE_NAME">%1$s</xliff:g>နှင့် တွဲချိတ်မရပါ။"</string>
@@ -159,8 +159,8 @@
<string name="oem_unlock_enable_summary" msgid="4720281828891618376">"အစပြုခြင်းကိရိယာအား သော့ဖွင့်ရန် ခွင့်ပြုမည်"</string>
<string name="confirm_enable_oem_unlock_title" msgid="4802157344812385674">"OEM သော့ဖွင့်ခြင်း ခွင့်ပြုမလား?"</string>
<string name="confirm_enable_oem_unlock_text" msgid="5517144575601647022">"သတိပေးချက်: ဤချိန်ညှိချက်ဖွင့်ထားလျှင်၊ ဤစက်ပစ္စည်းပေါ်တွင် စက်ပစ္စည်းကာကွယ်သည့် အထူးပြုလုပ်ချက် အလုပ်လုပ်မည်မဟုတ်ပါ။"</string>
- <string name="mock_location_app" msgid="7966220972812881854">"တည်နေရာအတုပြု အက်ပ် ရွေးရန်"</string>
- <string name="mock_location_app_not_set" msgid="809543285495344223">"တည်နေရာအတုပြ အက်ပ် သတ်မှတ်ထားခြင်းမရှိပါ"</string>
+ <string name="mock_location_app" msgid="7966220972812881854">"တည်နေရာအတုပြု အက်ပ်ရွေးရန်"</string>
+ <string name="mock_location_app_not_set" msgid="809543285495344223">"တည်နေရာအတုပြ အက်ပ်သတ်မှတ်ထားခြင်းမရှိပါ"</string>
<string name="mock_location_app_set" msgid="8966420655295102685">"တည်နေရာအတုပြ အက်ပ်- <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="debug_networking_category" msgid="7044075693643009662">"ကွန်ရက်လုပ်ငန်း"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"ကြိုးမဲ့ပြသမှု အသိအမှတ်ပြုလက်မှတ်"</string>
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"မျက်နှာပြင်တွင် ဖော်ပြသည့် အချက်လက် အသွားအလာ ပမာဏပေါ်တွင် အခြေခံ၍ WIFI ရွမ်းရှာဖွေမှုအား ဖွင့်/ပိတ်မည်"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"လော့ဂါး ဘာဖား ဆိုက်များ"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"လော့ ဘာဖားတွက် လော့ဂါးဆိုက် ရွေး"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"မှတ်တမ်းထိန်းသိမ်းပေးသည့် သိုလှောင်ခန်းကို ရှင်းလင်းမလား။"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"အမြဲတမ်းမှတ်တမ်းတင်ခြင်းစနစ်ဖြင့် ကျွန်ုပ်တို့ကစောင့်ကြည့်ခြင်းမရှိတော့သည့်အခါ သင့်စက်ပစ္စည်းပေါ်ရှိ ဒေတာမှတ်တမ်းစနစ်ကို ကျွန်ုပ်တို့က ဖျက်ရပါလိမ့်မည်။"</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"စက်တွင် မှတ်တမ်းအြမဲသိမ်းရန်"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"စက်ပစ္စည်းပေါ်တွင် ထိန်းသိမ်းသိုမှီးရန် မှတ်တမ်းလျာထားချက်များကို ရွေးပါ"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB စီစဉ်ဖွဲ့စည်းမှု ရွေးရန်"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB စီစဉ်ဖွဲ့စည်းမှု ရွေးရန်"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"ပုံစံတုတည်နေရာများကို ခွင့်ပြုရန်"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"ပုံစံတုတည်နေရာများကို ခွင့်ပြုရန်"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"အရည်အချင်းများ စူးစမ်းမှု မြင်ကွင်းကို ဖွင့်ပေးရန်"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Android DHCP ကလိုင်းယင့် အသစ် အစား Lollipop မှ DHCP ကလိုင်းယင့်အား သုံးပါ။"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"ဝိုင်ဖိုင်ဖွင့်ထားလျှင်တောင် မိုဘိုင်းဒေတာအမြဲတမ်းဖွင့်မည် (မြန်ဆန်သည့် ကွန်ရက် ပြောင်းခြင်းအတွက်)။"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB ပြသနာရှာခြင်း ခွင့်ပြုပါမလား?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USBအမှားရှားခြင်းမှာ ဆော့ဝဲလ်ရေးသားရန်အတွက်သာ ရည်ရွယ်ပါသည်။ သင့်ကွန်ပြုတာနှင့်သင့်စက်ကြားတွင် ဒေတာများကိုကူးယူရန်၊ အကြောင်းမကြားပဲနှင့် သင့်စက်အတွင်းသို့ အပလီကေးရှင်းများထည့်သွင်းခြင်းနှင့် ဒေတာမှတ်တမ်းများဖတ်ရန်အတွက် အသုံးပြုပါ"</string>
@@ -201,7 +204,7 @@
<string name="select_application" msgid="5156029161289091703">"အပလီကေးရှင်းရွေးချယ်ရန်"</string>
<string name="no_application" msgid="2813387563129153880">"တခုမှမရှိ"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"အပြစ်ရှာဖွေ ဖယ်ရှားချက်ကိုစောင့်ရန်"</string>
- <string name="wait_for_debugger_summary" msgid="1766918303462746804">"အမှားပြင်ဆင်ရှာဖွေသည့် အပလီကေးရှင်းသည် လုပ်ငန်းမစမှီ တွဲဖက်ရန် အမှားရှာဖွေမည့်သူကို စောင့်နေသည်။"</string>
+ <string name="wait_for_debugger_summary" msgid="1766918303462746804">"အမှားပြင်ဆင်ရှာဖွေသည့် အပလီကေးရှင်းသည် လုပ်ငန်းမစမီ တွဲဖက်ရန် အမှားရှာဖွေမည့်သူကို စောင့်နေသည်။"</string>
<string name="debug_input_category" msgid="1811069939601180246">"ထည့်သွင်းရန်"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"ရေးဆွဲခြင်း"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"ဟာ့ဒ်ဝဲ အရှိန်မြှင့် ပုံဖော်ခြင်း"</string>
@@ -214,11 +217,11 @@
<string name="show_touches" msgid="2642976305235070316">"တို့ခြင်းများကို ပြပါ"</string>
<string name="show_touches_summary" msgid="6101183132903926324">"တို့ခြင်းများအတွက် အမြင်ဖြင့် တုံ့ပြန်မှုပြပါ"</string>
<string name="show_screen_updates" msgid="5470814345876056420">"surface အဆင့်မြှင့်မှုများပြပါ"</string>
- <string name="show_screen_updates_summary" msgid="2569622766672785529">"အဆင့်မြှင့်နေစဉ် ဝင်းဒိုးမျက်နှာတပြင်လုံးကို အချက်ပြရန်"</string>
+ <string name="show_screen_updates_summary" msgid="2569622766672785529">"အပ်ဒိတ်လုပ်စဉ် ဝင်းဒိုးမျက်နှာပြင်တွင် အချက်ပြရန်"</string>
<string name="show_hw_screen_updates" msgid="5036904558145941590">"GPUမြင်ကွင်းအဆင့်မြှင့်ခြင်းများပြရန်"</string>
<string name="show_hw_screen_updates_summary" msgid="1115593565980196197">"GPU နှင့်ဆွဲစဉ် ၀င်းဒိုးအတွင်းပိုင်း လျှပ်တပြက်မြင်ကွင်းများ"</string>
<string name="show_hw_layers_updates" msgid="5645728765605699821">"ဟာ့ဒ်ဝဲအလွှာများအဆင်မြှင့်မှုကိုပြရန်"</string>
- <string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"အဆင့်မြှင်ချိန် ဟာ့ဒ်ဝဲအလွှာများကို အစိမ်းရောင်ဖြင့်အချက်ပြရန်"</string>
+ <string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"အပ်ဒိတ်လုပ်ချိန် ဟာ့ဒ်ဝဲအလွှာများ အစိမ်းရောင်ပြပါ"</string>
<string name="debug_hw_overdraw" msgid="2968692419951565417">"GPU ပိုသုံးစွဲမှုအမှားရှာဖွေပြင်ဆင်ရန်"</string>
<string name="disable_overlays" msgid="2074488440505934665">"HWထပ်ဆင့်အရာများပိတ်ရန်"</string>
<string name="disable_overlays_summary" msgid="3578941133710758592">"GPU ကိုမျက်နှာပြင်ခင်းကျင်းရာတွင် အမြဲသုံးပါ။"</string>
@@ -242,7 +245,7 @@
<string name="transition_animation_scale_title" msgid="387527540523595875">"သက်ဝင်အသွင်ပြောင်းခြင်း"</string>
<string name="animator_duration_scale_title" msgid="3406722410819934083">"လှုပ်ရှားမှုကြာချိန်စကေး"</string>
<string name="overlay_display_devices_title" msgid="5364176287998398539">"ဆင့်ပွားမျက်နှာပြင်များအသွင်ဆောင်သည်"</string>
- <string name="debug_applications_category" msgid="4206913653849771549">"အပလီကေးရှင်းများ"</string>
+ <string name="debug_applications_category" msgid="4206913653849771549">"အက်ပ်များ"</string>
<string name="immediately_destroy_activities" msgid="1579659389568133959">"ဆောင်ရွက်မှုများကို မသိမ်းထားပါနှင့်"</string>
<string name="immediately_destroy_activities_summary" msgid="3592221124808773368">"အသုံးပြုသူထွက်ခွါသွားသည်နှင့် လုပ်ဆောင်ချက်များကို ဖျက်ပစ်မည်"</string>
<string name="app_process_limit_title" msgid="4280600650253107163">"နောက်ခံလုပ်ငန်းစဉ်ကန့်သတ်ခြင်း"</string>
@@ -270,7 +273,7 @@
<item msgid="8280754435979370728">"မျက်လုံးမှတွေ့ရသည့် သဘာဝအရောင်"</item>
<item msgid="5363960654009010371">"ဒီဂျစ်တယ်အကြောင်းအရာအတွက် ပြင်ဆင်ထားသည့် အရောင်များ"</item>
</string-array>
- <string name="inactive_apps_title" msgid="1317817863508274533">"အလုပ်မလုပ်သော အက်ပ် များ"</string>
+ <string name="inactive_apps_title" msgid="1317817863508274533">"အလုပ်မလုပ်သော အက်ပ်များ"</string>
<string name="inactive_app_inactive_summary" msgid="5091363706699855725">"ပွင့်မနေပါ။ ပြောင်းရန်တို့ပါ။"</string>
<string name="inactive_app_active_summary" msgid="4174921824958516106">"ပွင့်နေသည်။ ပြောင်းရန်တို့ပါ။"</string>
<string name="runningservices_settings_title" msgid="8097287939865165213">"အလုပ်လုပ်နေသောဝန်ဆောင်မှုများ"</string>
@@ -284,7 +287,7 @@
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"ပြောင်းရန်…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ဖိုင်ကို လုံခြုံအောင်ပြုလုပ်ပြီးပါပြီ"</string>
<string name="title_convert_fbe" msgid="1263622876196444453">"ဖိုင်အခြေပြုလုံခြုံအောင်ပြုလုပ်ခြင်းသို့ ပြောင်းလဲရန်"</string>
- <string name="convert_to_fbe_warning" msgid="6139067817148865527">"အချက်အလက်အပိုင်းကို ဖိုင်အခြေပြုလုံခြုံအောင်ပြုလုပ်ခြင်းသို့ ပြောင်းပါ။\n !!သတိ!! ၎င်းသည်သင့် အချက်အလက်များအားလုံးကို ဖျက်ပါမည်။ \n ဤလုပ်ဆောင်ချက်သည် ပထမအဆင့်တွင်သာ ရှိသေးပြီး မှန်ကန်စွာ လုပ်ဆောင်လိမ့်မည် မဟုတ်ပါ။\n ရှေ့ဆက်ရန် \'ရှင်းလင်းပြီး ပြောင်းလဲရန်…\' ကိုနှိပ်ပါ။"</string>
+ <string name="convert_to_fbe_warning" msgid="6139067817148865527">"အချက်အလက်အပိုင်းကို ဖိုင်အခြေပြုလုံခြုံအောင်ပြုလုပ်ခြင်းသို့ ပြောင်းပါ။\n !!သတိ!! ၎င်းသည်သင့် အချက်အလက်များအားလုံးကို ဖျက်ပါမည်။ \n ဤလုပ်ဆောင်ချက်သည် ပထမအဆင့်တွင်သာ ရှိသေးပြီး မှန်ကန်စွာ လုပ်ဆောင်လိမ့်မည် မဟုတ်ပါ။\n ဆက်လုပ်ရန် \'ရှင်းလင်းပြီး ပြောင်းလဲရန်…\' ကိုနှိပ်ပါ။"</string>
<string name="button_convert_fbe" msgid="5152671181309826405">"ရှင်းလင်းပြီး ပြောင်းလဲရန်…"</string>
<string name="picture_color_mode" msgid="4560755008730283695">"ဓာတ်ပုံအရောင်မုဒ်"</string>
<string name="picture_color_mode_desc" msgid="1141891467675548590">"sRGB ကို အသုံးပြုပါ"</string>
diff --git a/packages/SettingsLib/res/values-nb/arrays.xml b/packages/SettingsLib/res/values-nb/arrays.xml
index 8311db3..bc4ff27 100644
--- a/packages/SettingsLib/res/values-nb/arrays.xml
+++ b/packages/SettingsLib/res/values-nb/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4M per loggbuffer"</item>
<item msgid="5431354956856655120">"16M per loggbuffer"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Av"</item>
+ <item msgid="3054662377365844197">"Alle"</item>
+ <item msgid="688870735111627832">"Unntatt radio"</item>
+ <item msgid="2850427388488887328">"bare kjerne"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Av"</item>
+ <item msgid="172978079776521897">"Alle loggbuffere"</item>
+ <item msgid="3873873912383879240">"Alle unntatt radiologgbuffere"</item>
+ <item msgid="8489661142527693381">"bare kjerneloggbuffer"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animasjon av"</item>
<item msgid="6624864048416710414">"Animasjonsskala 0,5 x"</item>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 1c13fc3..5d8ae55 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -98,7 +98,7 @@
<string name="launch_defaults_some" msgid="313159469856372621">"Noen standardvalg er angitt"</string>
<string name="launch_defaults_none" msgid="4241129108140034876">"Ingen standardvalg er angitt"</string>
<string name="tts_settings" msgid="8186971894801348327">"Talesyntese-kontroller"</string>
- <string name="tts_settings_title" msgid="1237820681016639683">"Tekst-til-tale"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Tekst til tale"</string>
<string name="tts_default_rate_title" msgid="6030550998379310088">"Talehastighet"</string>
<string name="tts_default_rate_summary" msgid="4061815292287182801">"Hvor raskt teksten leses"</string>
<string name="tts_default_pitch_title" msgid="6135942113172488671">"Stemmeleie"</string>
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Tillat / ikke tillat skanning for Wi-Fi-roaming basert på mengden datatrafikk til stede i grensesnittet"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Bufferstørrelser for logg"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Velg loggstørrelse per loggbuffer"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Vil du tømme det varige logglageret?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Når vi ikke lenger overvåker med den varige loggeren, kreves det at vi tømmer loggdataene som ligger på enheten din."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Lagre loggdata varig på enheten"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Velg loggbufferne som skal lagres varig på enheten"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Velg USB-konfigurasjon"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Velg USB-konfigurasjon"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Tillat simulert posisjon"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Tillat bruk av simulerte GPS-koordinater"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Slå på inspeksjon av visningsattributt"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Bruk DHCP-klienten fra Lollipop i stedet for den nye DHCP-klienten for Android."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Ha alltid mobildata slått på, selv når Wi-Fi er aktiv (for hurtig nettverksbytting)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Tillate USB-feilsøking?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB-feilsøking er bare ment for utviklingsformål. Bruk det til å kopiere data mellom datamaskinen og enheten, installere apper på enheten uten varsel og lese loggdata."</string>
diff --git a/packages/SettingsLib/res/values-ne-rNP/arrays.xml b/packages/SettingsLib/res/values-ne-rNP/arrays.xml
index 7468982..2063f17 100644
--- a/packages/SettingsLib/res/values-ne-rNP/arrays.xml
+++ b/packages/SettingsLib/res/values-ne-rNP/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"४एम प्रति लग बफर"</item>
<item msgid="5431354956856655120">"१६एम प्रति लग बफर"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"निष्क्रिय"</item>
+ <item msgid="3054662377365844197">"सबै"</item>
+ <item msgid="688870735111627832">"रेडियो बाहेक सबै"</item>
+ <item msgid="2850427388488887328">"कर्नेल मात्र"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"निष्क्रिय"</item>
+ <item msgid="172978079776521897">"सबै लग सम्बन्धी बफरहरू"</item>
+ <item msgid="3873873912383879240">"रेडियो सम्बन्धी लगका बफरहरू बाहेक सबै"</item>
+ <item msgid="8489661142527693381">"कर्नेलको लग सम्बन्धी बफर मात्र"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"सजीविकरण बन्द"</item>
<item msgid="6624864048416710414">"सजीविकरण मापन .5x"</item>
diff --git a/packages/SettingsLib/res/values-ne-rNP/strings.xml b/packages/SettingsLib/res/values-ne-rNP/strings.xml
index f8ba831..15cc8ea8 100644
--- a/packages/SettingsLib/res/values-ne-rNP/strings.xml
+++ b/packages/SettingsLib/res/values-ne-rNP/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Wi-Fi घुम्ने स्क्यान इन्टरफेसमा रहेको डेटा यातायातको मात्रामा आधारित अनुमति दिनुहोस्/नदिनुहोस्"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"लगर बफर आकारहरू"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"लग बफर प्रति लगर आकार चयन गर्नुहोस्"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"लगरको निरन्तर भण्डारणलाई खाली गर्ने हो?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"हामी अब निरन्तर लगर मार्फत अनुगमन गरिरहेका छैनौँ, त्यसैले हामीले तपाईँको यन्त्रमा रहेको लगर सम्बन्धी डेटा मेटाउन आवश्यक छ।"</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"लगर सम्बन्धी डेटालाई निरन्तर यन्त्रमा भण्डारण गर्नुहोस्"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"यन्त्रमा निरन्तर भण्डारण गरिने लग सम्बन्धी बफरहरूलाई चयन गर्नुहोस्"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB विन्यास चयन गर्नुहोस्"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB विन्यास चयन गर्नुहोस्"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"नक्कली स्थानहरूलाई अनुमति दिनुहोस्"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"नक्कली स्थानहरूलाई अनुमति दिनुहोस्"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"दृष्टिकोण विशेषता निरीक्षण सक्षम पार्नुहोस्"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"नयाँ Android DHCP ग्राहकको सट्टा Lollipop बाट DHCP ग्राहक प्रयोग गर्नुहोस्।"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Wi-Fi सक्रिय हुँदा पनि मोबाइल डेटा सधैँ सक्रिय राख्नुहोस् (द्रूत नेटवर्क स्विच गर्नको लागि)।"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB डिबग गर्न लागि अनुमति दिने हो?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"युएसबी डिबगिङ विकास प्रयोजनका लागि मात्र निर्मित हुन्छ। यसलाई तपाईँको कम्प्युटर र तपाईँको उपकरणका बीच डेटा प्रतिलिपि गर्न, बिना सूचना तपाईँको उपकरणमा अनुप्रयोगहरू स्थापना गर्न र लग डेटा पढ्नका लागि प्रयोग गर्नुहोस्।"</string>
diff --git a/packages/SettingsLib/res/values-nl/arrays.xml b/packages/SettingsLib/res/values-nl/arrays.xml
index 48b135d..4cdd4fa 100644
--- a/packages/SettingsLib/res/values-nl/arrays.xml
+++ b/packages/SettingsLib/res/values-nl/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 M per logbuffer"</item>
<item msgid="5431354956856655120">"16 M per logbuffer"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Uit"</item>
+ <item msgid="3054662377365844197">"Alle"</item>
+ <item msgid="688870735111627832">"Alle beh. keuzerondje"</item>
+ <item msgid="2850427388488887328">"alleen kernel"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Uit"</item>
+ <item msgid="172978079776521897">"Alle logboekbuffers"</item>
+ <item msgid="3873873912383879240">"Alle logboekbuffers behalve voor keuzerondjes"</item>
+ <item msgid="8489661142527693381">"alleen kernel-logbuffer"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animatie uit"</item>
<item msgid="6624864048416710414">"Animatieschaal 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index b080131..12bdb4f 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Roamingscans voor wifi (niet) toestaan op basis van de hoeveelheid dataverkeer die aanwezig is bij de interface"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Logger-buffergrootten"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Kies Logger-grootten per logbuffer"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Persistente loggeropslag wissen?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Wanneer we niet meer controleren met de persistente logger, zijn we verplicht de logger-gegevens op je apparaat te wissen."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Logger-gegev. persistent opslaan"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Logboekbuffers selecteren die persistent op het apparaat worden opgeslagen"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB-configuratie selecteren"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB-configuratie selecteren"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Neplocaties toestaan"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Neplocaties toestaan"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Inspectie van weergavekenmerk inschakelen"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"De DHCP-client van Lollipop gebruiken in plaats van de nieuwe Android DHCP-client."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Mobiele gegevens altijd actief houden, ook als wifi actief is (voor sneller schakelen tussen netwerken)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB-foutopsporing toestaan?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB-foutopsporing is alleen bedoeld voor ontwikkeldoeleinden. Het kan worden gebruikt om gegevens te kopiëren tussen je computer en je apparaat, apps zonder melding op je apparaat te installeren en loggegevens te lezen."</string>
diff --git a/packages/SettingsLib/res/values-pa-rIN/arrays.xml b/packages/SettingsLib/res/values-pa-rIN/arrays.xml
index 42cc9f2..d0a26c8 100644
--- a/packages/SettingsLib/res/values-pa-rIN/arrays.xml
+++ b/packages/SettingsLib/res/values-pa-rIN/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4M ਪ੍ਰਤੀ ਲੌਗ ਬਫਰ"</item>
<item msgid="5431354956856655120">"16M ਪ੍ਰਤੀ ਲੌਗ ਬਫਰ"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"ਬੰਦ"</item>
+ <item msgid="3054662377365844197">"ਸਭ"</item>
+ <item msgid="688870735111627832">"ਸਭ ਪਰ ਰੇਡੀਓ"</item>
+ <item msgid="2850427388488887328">"ਸਿਰਫ਼ ਕਰਨਲ"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"ਬੰਦ"</item>
+ <item msgid="172978079776521897">"ਸਭ ਲੌਗ ਬਫ਼ਰ"</item>
+ <item msgid="3873873912383879240">"ਸਭ ਪਰ ਰੇਡੀਓ ਲੌਗ ਬਫ਼ਰ"</item>
+ <item msgid="8489661142527693381">"ਸਿਰਫ਼ ਕਰਨਲ ਲੌਗ ਬਫ਼ਰ"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"ਐਨੀਮੇਸ਼ਨ ਬੰਦ"</item>
<item msgid="6624864048416710414">"ਐਨੀਮੇਸ਼ਨ ਸਕੇਲ .5x"</item>
diff --git a/packages/SettingsLib/res/values-pa-rIN/strings.xml b/packages/SettingsLib/res/values-pa-rIN/strings.xml
index f2bb190..21d11b0 100644
--- a/packages/SettingsLib/res/values-pa-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-pa-rIN/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"ਇੰਟਰਫੇਸ ਤੇ ਮੌਜੂਦ ਡੈਟਾ ਟ੍ਰੈਫਿਕ ਦੀ ਮਾਤਰਾ ਦੇ ਆਧਾਰ ਤੇ Wi‑Fi ਰੋਮ ਸਕੈਨ ਦੀ ਆਗਿਆ ਦਿਓ/ਅਸਵੀਕਾਰ ਕਰੋ"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"ਲੌਗਰ ਬਫਰ ਆਕਾਰ"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"ਪ੍ਰਤੀ ਲੌਗ ਬਫਰ ਲੌਗਰ ਆਕਾਰ ਚੁਣੋ"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"ਕੀ ਲੌਗਰ ਪ੍ਰਸਿੱਸਟੈਂਟ ਸਟੋਰੇਜ ਨੂੰ ਸਾਫ਼ ਕਰਨਾ ਹੈ?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"ਜਦੋਂ ਅਸੀਂ ਪ੍ਰਸਿੱਸਟੈਂਟ ਲੌਗਰ ਨਾਲ ਨਿਗਰਾਨੀ ਨਹੀਂ ਕਰ ਰਹੇ ਹੁੰਦੇ ਹਾਂ, ਤਾਂ ਸਾਨੂੰ ਤੁਹਾਡੀ ਡੀਵਾਈਸ ਵਿੱਚ ਮੌਜੂਦ ਲੌਗਰ ਡੈਟੇ ਨੂੰ ਮਿਟਾਉਣ ਦੀ ਲੋੜ ਪੈਂਦੀ ਹੈ।"</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"ਡੀਵਾਈਸ \'ਤੇ ਲੌਗ ਬਫ਼ਰਾਂ ਨੂੰ ਸਥਾਈ ਤੌਰ \'ਤੇ ਸਟੋਰ ਕਰੋ"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"ਡੀਵਾਈਸ \'ਤੇ ਸਥਾਈ ਤੌਰ \'ਤੇ ਸਟੋਰ ਕਰਨ ਲਈ ਲੌਗ ਬਫ਼ਰਾਂ ਨੂੰ ਚੁਣੋ"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB ਕੌਂਫਿਗਰੇਸ਼ਨ ਚੁਣੋ"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB ਕੌਂਫਿਗਰੇਸ਼ਨ ਚੁਣੋ"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"ਨਕਲੀ ਨਿਰਧਾਰਿਤ ਸਥਾਨਾਂ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"ਨਕਲੀ ਨਿਰਧਾਰਿਤ ਸਥਾਨਾਂ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"ਗੁਣ ਛਾਣਬੀਣ ਦੇਖੋ ਨੂੰ ਸਮਰੱਥ ਬਣਾਓ"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"ਨਵੇਂ Android DHCP ਕਲਾਈਂਟ ਦੀ ਬਜਾਇ Lollipop ਦਾ DHCP ਕਲਾਈਂਟ ਵਰਤੋ।"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"ਹਮੇਸ਼ਾ ਮੋਬਾਈਲ ਡੇਟਾ ਨੂੰ ਕਿਰਿਆਸ਼ੀਲ ਰੱਖੋ ਭਾਵੇਂ Wi‑Fi ਕਿਰਿਆਸ਼ੀਲ ਹੋਵੇ (ਤੇਜ਼ ਨੈੱਟਵਰਕ ਸਵਿੱਚਿੰਗ ਲਈ)।"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"ਕੀ USB ਡੀਬਗਿੰਗ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB ਡੀਬਗਿੰਗ ਕੇਵਲ ਵਿਕਾਸ ਮੰਤਵਾਂ ਲਈ ਹੁੰਦੀ ਹੈ। ਇਸਨੂੰ ਆਪਣੇ ਕੰਪਿਊਟਰ ਅਤੇ ਆਪਣੀ ਡੀਵਾਈਸ ਵਿਚਕਾਰ ਡੈਟਾ ਕਾਪੀ ਕਰਨ ਲਈ ਵਰਤੋ, ਸੂਚਨਾ ਦੇ ਬਿਨਾਂ ਆਪਣੀ ਡੀਵਾਈਸ ਤੇ ਐਪਸ ਇੰਸਟੌਲ ਕਰੋ ਅਤੇ ਲੌਗ ਡੈਟਾ ਪੜ੍ਹੋ।"</string>
diff --git a/packages/SettingsLib/res/values-pl/arrays.xml b/packages/SettingsLib/res/values-pl/arrays.xml
index 3b3e95d..ff36e42 100644
--- a/packages/SettingsLib/res/values-pl/arrays.xml
+++ b/packages/SettingsLib/res/values-pl/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 MB/bufor dziennika"</item>
<item msgid="5431354956856655120">"16 MB/bufor dziennika"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Wyłączone"</item>
+ <item msgid="3054662377365844197">"Wszystkie"</item>
+ <item msgid="688870735111627832">"Bez radiowych"</item>
+ <item msgid="2850427388488887328">"tylko jądro"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Wyłączone"</item>
+ <item msgid="172978079776521897">"Wszystkie bufory dziennika"</item>
+ <item msgid="3873873912383879240">"Wszystkie oprócz buforów dzienników radiowych"</item>
+ <item msgid="8489661142527693381">"tylko bufor dziennika jądra"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animacja wyłączona"</item>
<item msgid="6624864048416710414">"Skala animacji 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index c72f0c6..d5116a2 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Zezwalaj/nie zezwalaj na wyszukiwanie sieci Wi-Fi w roamingu w zależności od natężenia ruchu"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Rozmiary bufora Rejestratora"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Wybierz rozmiary Rejestratora/bufor dziennika"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Wyczyścić pamięć trwałych dzienników?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Po zakończeniu monitorowania przy użyciu trwale zapisywanych dzienników musimy usunąć ich dane zapisane na urządzeniu."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Zapisuj trwale dane dzienników"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Wybierz bufory dziennika, które mają być trwale przechowywane na urządzeniu"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Wybierz konfigurację USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Wybierz konfigurację USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Pozorowanie lokalizacji"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Zezwalaj na pozorowanie lokalizacji"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Inspekcja wyświetlania atrybutu"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Użyj klienta DHCP z Lollipop zamiast nowego klienta DHCP Androida"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Nie wyłączaj transmisji danych przez sieć komórkową, nawet gdy aktywne jest połączenie Wi-Fi (aby szybko przełączać sieci)"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Czy zezwalać na debugowanie USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Debugowanie USB jest przeznaczone wyłącznie do celów programistycznych. Może służyć do kopiowania danych między komputerem a urządzeniem, instalowania aplikacji na urządzeniu bez powiadamiania, a także odczytu danych dziennika."</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/arrays.xml b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
index 0d94a6d..90f061c 100644
--- a/packages/SettingsLib/res/values-pt-rBR/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 M/buffer de log"</item>
<item msgid="5431354956856655120">"16 M/buffer de log"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Desativado"</item>
+ <item msgid="3054662377365844197">"Todos"</item>
+ <item msgid="688870735111627832">"Todos, exceto o rádio"</item>
+ <item msgid="2850427388488887328">"somente kernel"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Desativado"</item>
+ <item msgid="172978079776521897">"Todos os buffers de registro"</item>
+ <item msgid="3873873912383879240">"Todos, exceto os buffers de registro de rádio"</item>
+ <item msgid="8489661142527693381">"somente buffer de registro de kernel"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animação desligada"</item>
<item msgid="6624864048416710414">"Escala da animação 0,5 x"</item>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index d01ddb7..f0cfa23 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Permitir/proibir verificações de roaming de Wi-Fi com base no volume do tráfego de dados presente na interface"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Tamanhos de buffer de logger"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Sel. tam. de logger/buffer de log"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Limpar armazenamento de logger constante?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Quando não estivermos mais monitorando com o logger constante, devemos limpar o residente de dados de logger do seu dispositivo."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Armazenar dados de logger constantemente no dispositivo"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Selecionar buffers de registro para armazenar constantemente no dispositivo"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Selecionar configuração USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Selecionar configuração USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Permitir locais fictícios"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Permitir locais fictícios"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Ativar visualiz. insp. atributo"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Usar cliente DHCP do Lollipop, em vez do novo cliente DHCP do Android."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Sempre manter dados móveis ativos, mesmo quando o Wi-Fi estiver ativado (para troca rápida de rede)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Permitir a depuração USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"A depuração USB serve apenas para fins de desenvolvimento. Use-a para copiar dados entre o computador e o dispositivo, instalar apps no seu aparelho sem notificação e ler dados de registro."</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/arrays.xml b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
index 6e84fce..2c8b835 100644
--- a/packages/SettingsLib/res/values-pt-rPT/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 M por buffer de registo"</item>
<item msgid="5431354956856655120">"16 M por buffer de registo"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Desativado"</item>
+ <item msgid="3054662377365844197">"Todos"</item>
+ <item msgid="688870735111627832">"Td, exc. rádio"</item>
+ <item msgid="2850427388488887328">"apenas kernel"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Desativado"</item>
+ <item msgid="172978079776521897">"Todos os buffers de registo"</item>
+ <item msgid="3873873912383879240">"Todos, exceto os buffers de registo de rádio"</item>
+ <item msgid="8489661142527693381">"apenas buffer do registo do kernel"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animação desativada"</item>
<item msgid="6624864048416710414">"Escala de animação 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 8e05d8c..40dfc74 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Permitir/impedir a deteção de Wi-Fi em roaming com base na quantidade de tráfego de dados presente na interface"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Tamanhos da memória intermédia do registo"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Selec. tam. reg. p/ mem. int. reg."</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Pretende limpar o armazenamento persistente do registo?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Quando deixamos de monitorizar com o registo persistente, é necessário apagar os dados de registo que se encontram no seu dispositivo."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Arm. dados de registo persist. no disp."</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Selecionar buffers de registo para armazenamento persistente no dispositivo"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Selecionar configuração USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Selecionar configuração USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Permitir locais fictícios"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Permitir locais fictícios"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Ativar a inspeção do atributo de visualização"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Utilizar o cliente DHCP do Lollipop em vez do novo cliente DHCP do Android."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Manter sempre os dados móveis ativados, mesmo quando o Wi‑Fi estiver ativado (para mudança de rede rápida)"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Permitir depuração USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"A depuração USB é utilizada apenas para fins de programação. Utilize-a para copiar dados entre o computador e o aparelho, instalar aplicações no aparelho sem notificação e ler dados de registo."</string>
@@ -222,7 +225,7 @@
<string name="debug_hw_overdraw" msgid="2968692419951565417">"Depurar sobreposição GPU"</string>
<string name="disable_overlays" msgid="2074488440505934665">"Desativ. sobreposições HW"</string>
<string name="disable_overlays_summary" msgid="3578941133710758592">"Utilizar sempre GPU para a composição do ecrã"</string>
- <string name="simulate_color_space" msgid="6745847141353345872">"Simular espaço de cor"</string>
+ <string name="simulate_color_space" msgid="6745847141353345872">"Simular espaço da cor"</string>
<string name="enable_opengl_traces_title" msgid="6790444011053219871">"Ativar vestígios OpenGL"</string>
<string name="usb_audio_disable_routing" msgid="8114498436003102671">"Desativ. encam. áudio USB"</string>
<string name="usb_audio_disable_routing_summary" msgid="980282760277312264">"Desativar encam. auto. para periféricos áudio USB"</string>
diff --git a/packages/SettingsLib/res/values-pt/arrays.xml b/packages/SettingsLib/res/values-pt/arrays.xml
index 0d94a6d..90f061c 100644
--- a/packages/SettingsLib/res/values-pt/arrays.xml
+++ b/packages/SettingsLib/res/values-pt/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 M/buffer de log"</item>
<item msgid="5431354956856655120">"16 M/buffer de log"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Desativado"</item>
+ <item msgid="3054662377365844197">"Todos"</item>
+ <item msgid="688870735111627832">"Todos, exceto o rádio"</item>
+ <item msgid="2850427388488887328">"somente kernel"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Desativado"</item>
+ <item msgid="172978079776521897">"Todos os buffers de registro"</item>
+ <item msgid="3873873912383879240">"Todos, exceto os buffers de registro de rádio"</item>
+ <item msgid="8489661142527693381">"somente buffer de registro de kernel"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animação desligada"</item>
<item msgid="6624864048416710414">"Escala da animação 0,5 x"</item>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index d01ddb7..f0cfa23 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Permitir/proibir verificações de roaming de Wi-Fi com base no volume do tráfego de dados presente na interface"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Tamanhos de buffer de logger"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Sel. tam. de logger/buffer de log"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Limpar armazenamento de logger constante?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Quando não estivermos mais monitorando com o logger constante, devemos limpar o residente de dados de logger do seu dispositivo."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Armazenar dados de logger constantemente no dispositivo"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Selecionar buffers de registro para armazenar constantemente no dispositivo"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Selecionar configuração USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Selecionar configuração USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Permitir locais fictícios"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Permitir locais fictícios"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Ativar visualiz. insp. atributo"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Usar cliente DHCP do Lollipop, em vez do novo cliente DHCP do Android."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Sempre manter dados móveis ativos, mesmo quando o Wi-Fi estiver ativado (para troca rápida de rede)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Permitir a depuração USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"A depuração USB serve apenas para fins de desenvolvimento. Use-a para copiar dados entre o computador e o dispositivo, instalar apps no seu aparelho sem notificação e ler dados de registro."</string>
diff --git a/packages/SettingsLib/res/values-ro/arrays.xml b/packages/SettingsLib/res/values-ro/arrays.xml
index 1f0e05b..d5574dd 100644
--- a/packages/SettingsLib/res/values-ro/arrays.xml
+++ b/packages/SettingsLib/res/values-ro/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 MB/zonă-tampon de înregistrări în jurnal"</item>
<item msgid="5431354956856655120">"16 MB/zonă-tampon de înregistrări în jurnal"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Dezactivată"</item>
+ <item msgid="3054662377365844197">"Toate"</item>
+ <item msgid="688870735111627832">"Toate, fără radio"</item>
+ <item msgid="2850427388488887328">"numai nucleul"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Dezactivată"</item>
+ <item msgid="172978079776521897">"Toate zonele-tampon pentru jurnale"</item>
+ <item msgid="3873873912383879240">"Toate zonele-tampon pentru jurnale fără cele radio"</item>
+ <item msgid="8489661142527693381">"numai zona-tampon pentru jurnalul nucleului"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animație dezactivată"</item>
<item msgid="6624864048416710414">"Animație la scara 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index c8587ef..6cc0f87 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Permiteți/Nu permiteți scanarea traficului Wi-Fi în funcție de traficul de date din interfață"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Dimensiunile tamponului jurnalului"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Dimensiuni jurnal / tampon jurnal"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Ștergeți stocarea permanentă a jurnalului?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Când nu mai monitorizăm folosind jurnalul permanent, trebuie să ștergem datele de jurnal aflate pe dispozitivul dvs."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Stocați date jurnal permanent pe dispozitiv"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Selectați zonele-tampon ale jurnalului de stocat permanent pe dispozitiv"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Selectați configurația USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Selectați configurația USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Permiteți locațiile fictive"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Permiteți locațiile fictive"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Activați inspectarea atributelor de vizualizare"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Folosiți clientul DHCP din Lollipop în locul noului client Android DHCP."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Păstrați întotdeauna conexiunea de date mobile activată, chiar și atunci când funcția Wi‑Fi este activată (pentru comutarea rapidă între rețele)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Permiteți depanarea USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Depanarea USB are exclusiv scopuri de dezvoltare. Utilizați-o pentru a copia date de pe computer pe dispozitiv, pentru a instala aplicații pe dispozitiv fără notificare și pentru a citi datele din jurnale."</string>
diff --git a/packages/SettingsLib/res/values-ru/arrays.xml b/packages/SettingsLib/res/values-ru/arrays.xml
index 78965ac..2fff9dd 100644
--- a/packages/SettingsLib/res/values-ru/arrays.xml
+++ b/packages/SettingsLib/res/values-ru/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"Буфер: макс. 4 МБ"</item>
<item msgid="5431354956856655120">"Буфер: макс. 16 МБ"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Отключено"</item>
+ <item msgid="3054662377365844197">"Все"</item>
+ <item msgid="688870735111627832">"Все, кроме системных"</item>
+ <item msgid="2850427388488887328">"только ядро"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Отключено"</item>
+ <item msgid="172978079776521897">"Все буферы журналов"</item>
+ <item msgid="3873873912383879240">"Все, кроме буферов системного журнала"</item>
+ <item msgid="8489661142527693381">"только буфер журнала ядра"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Без анимации"</item>
<item msgid="6624864048416710414">"Анимация 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 5721a8d..ad4db89 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Включать или отключать поиск сетей Wi-Fi во время передачи данных в зависимости от объема трафика"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Размер буфера журнала"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Выберите размер буфера журнала"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Очистить постоянный диск журнала?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Прекратив отслеживание постоянного журнала, мы будем обязаны удалить его данные, сохраненные на устройстве."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Постоянно хранить данные журнала на устройстве"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Выберите буферы журнала, которые будут постоянно храниться на устройстве"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Конфигурация USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Конфигурация USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Фиктивные местоположения"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Разрешить использование фиктивных местоположений"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Включить проверку атрибутов"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Использовать DHCP-клиент для Android 5.0, а не для новой версии."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Не отключать передачу данных по мобильной сети даже при активном Wi-Fi-подключении (для быстрого переключения между сетями)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Разрешить отладку USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Отладка по USB – это режим, который позволяет использовать ваше устройство как внешний накопитель: перемещать файлы (с компьютера и на компьютер), напрямую устанавливать приложения, а также просматривать системные журналы."</string>
diff --git a/packages/SettingsLib/res/values-si-rLK/arrays.xml b/packages/SettingsLib/res/values-si-rLK/arrays.xml
index ab30e45..518a330 100644
--- a/packages/SettingsLib/res/values-si-rLK/arrays.xml
+++ b/packages/SettingsLib/res/values-si-rLK/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"ලොග අන්තරාවකට 4M"</item>
<item msgid="5431354956856655120">"ලොග අන්තරාවකට 16M"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"ක්රියාවිරහිතය"</item>
+ <item msgid="3054662377365844197">"සියලු"</item>
+ <item msgid="688870735111627832">"සැම වුවද රේඩි."</item>
+ <item msgid="2850427388488887328">"කර්නල පමණි"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"ක්රියාවිරහිතයි"</item>
+ <item msgid="172978079776521897">"සියලු ලොග අන්තරාචය"</item>
+ <item msgid="3873873912383879240">"සියලු නමුත් රේඩියෝ ලොග අන්තරාචය"</item>
+ <item msgid="8489661142527693381">"කර්නල ලොග් අන්තරාචය පමණි"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"සජිවිකරණය අක්රිය කිරීම"</item>
<item msgid="6624864048416710414">"සජීවීකරණ පරිමාණය .5x"</item>
diff --git a/packages/SettingsLib/res/values-si-rLK/strings.xml b/packages/SettingsLib/res/values-si-rLK/strings.xml
index 0087352..5efb400 100644
--- a/packages/SettingsLib/res/values-si-rLK/strings.xml
+++ b/packages/SettingsLib/res/values-si-rLK/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"අතුරු මුහුණතෙහි ඇති දත්ත තදබදය අනුව Wi‑Fi රෝම් පරිලෝකන වෙත ඉඩ දෙන්න/නොදෙන්න"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"ලෝගයේ අන්තරාවක ප්රමාණය"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"ලොග අන්තරාවකට ලෝගයේ ප්රමාණය තෝරන්න"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"ලොගකරු නොනවතින ගබඩාව හිස් කරන්නද?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"අප තවදුරටත් නොනවතින ලොගකරුවකු සමගින් නිරීක්ෂණය නොකරන විට, අපට ඔබේ උපාංගය මත නේවාසික ලොගකරු දත්ත මැකීමට අවශ්ය වේ."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"උපාංගයේ දිගටම ලොග දත්ත ගබඩා කි."</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"උපාංගය මත නොනැවතී ගබඩා කිරීමට ලොග අන්තරාචය තෝරන්න"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB වින්යාස දත්ත තෝරන්න"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB වින්යාස දත්ත තෝරන්න"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"ව්යාජ ස්ථානයන්ට අවසර දෙන්න"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"ව්යාජ ස්ථාන අනුමත කරන්න"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"උපලක්ෂණ පරික්ෂාව බැලීම සබල කරන්න"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"නව Android DHCP සේවාලාභියා වෙනුවට Lollipop වෙතින් DHCP සේවාලාභියා භාවිත කරන්න."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Wi‑Fi අක්රිය විට පවා, සැම විටම ජංගම දත්ත ක්රියාකාරීව තබන්න (අවසන් ජාල මාරුව සඳහා)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB දෝශාවේක්ෂණයට ඉඩ දෙන්නද?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB දෝශාවේක්ෂණය සංවර්ධන කටයුතු සඳහා පමණක් යොදාගැනේ. එය ඔබගේ පරිගණකය සහ ඔබගේ උපාංගය අතර දත්ත පිටපත් කිරීමට පමණක් භාවිතා කරන්න, ඔබගේ උපාංගය මත දැනුම්දීම් රහිතව යෙදුම් ස්ථාපනය කරන්න, සහ ලොග් දත්ත කියවන්න."</string>
diff --git a/packages/SettingsLib/res/values-sk/arrays.xml b/packages/SettingsLib/res/values-sk/arrays.xml
index 9a56e78..2848040 100644
--- a/packages/SettingsLib/res/values-sk/arrays.xml
+++ b/packages/SettingsLib/res/values-sk/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 MB na vyrov. pamäť denníka"</item>
<item msgid="5431354956856655120">"16 MB na vyrov. pamäť denníka"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Vypnuté"</item>
+ <item msgid="3054662377365844197">"Všetko"</item>
+ <item msgid="688870735111627832">"Okrem rádia"</item>
+ <item msgid="2850427388488887328">"iba jadro"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Vypnuté"</item>
+ <item msgid="172978079776521897">"Všetky vyrovnávacie pamäte denníka"</item>
+ <item msgid="3873873912383879240">"Všetky vyrovnávacie pamäte denníka (okrem rádia)"</item>
+ <item msgid="8489661142527693381">"iba vyrovnávacia pamäť denníka jadra"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animácia je vypnutá"</item>
<item msgid="6624864048416710414">"Mierka animácie 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index a791a1e..7a7e3d4 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Povoliť alebo zakázať funkciu Wifi Roam Scans na základe objemu prenosu údajov v rozhraní"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Veľkosti vyrovnávacej pamäte denníka"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Veľkosť na vyrovnávaciu pamäť nástroja denníkov"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Vymazať trvalé úložisko zapisovača do denníka?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Keď prestaneme monitorovať pomocou trvalého zapisovača do denníka, musíme vymazať jeho dáta, ktoré sa nachádzajú vo vašom zariadení."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Natrvalo ukladať dáta zapisovača do denníka na zariadení"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Vybrať vyrovnávacie pamäte denníka na trvalé ukladanie údajov na zariadení"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Výber konfigurácie USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Výber konfigurácie USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Povoliť simulované polohy"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Povoliť simulované polohy"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Kontrola atribútov zobrazenia"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Použitie klienta DHCP z verzie Lollipop namiesto nového klienta Android DHCP."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Vždy ponechávať mobilné dáta aktívne, dokonca aj pri aktívnej sieti Wi‑Fi (na rýchle prepínanie sietí)"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Povoliť ladenie cez USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Ladenie prostredníctvom USB je určené iba na účely vývoja. Použite ho na kopírovanie dát medzi počítačom a zariadením, inštaláciu aplikácií do zariadenia bez upozornenia a čítanie údajov denníka."</string>
diff --git a/packages/SettingsLib/res/values-sl/arrays.xml b/packages/SettingsLib/res/values-sl/arrays.xml
index 11b2bab..baa16ac 100644
--- a/packages/SettingsLib/res/values-sl/arrays.xml
+++ b/packages/SettingsLib/res/values-sl/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 M/medpomnilnik dnevnika"</item>
<item msgid="5431354956856655120">"16 M/medpomnilnik dnevnika"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Izklopljeno"</item>
+ <item msgid="3054662377365844197">"Vse"</item>
+ <item msgid="688870735111627832">"Vse (brez rad.)"</item>
+ <item msgid="2850427388488887328">"samo jedro"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Izklopljeno"</item>
+ <item msgid="172978079776521897">"Vsi medpomnilniki dnevnika"</item>
+ <item msgid="3873873912383879240">"Vsi medpomnilniki dnevnika, razen za radio"</item>
+ <item msgid="8489661142527693381">"samo medpomnilnik dnevnika jedra"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animacija je izključena"</item>
<item msgid="6624864048416710414">"Merilo animacije: 0,5 x"</item>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 25972d0..10bff6e 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Omogoči/onemogoči iskanje omrežij Wi-Fi za gostovanje glede na količino podatkovnega prometa pri vmesniku"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Velikosti medpomn. zapisov. dnevnika"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Izberite velikost medpomnilnika dnevnika"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Želite izbrisati trajno shranjevanje dnevniškega orodja?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Ko prenehamo spremljanje s trajnim dnevniškim orodjem, moramo podatke tega orodja, shranjene v napravi, izbrisati."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Trajno shranjevanje podatkov dnevniškega orodja v napravi"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Izberite, katere medpomnilnike dnevnika želite trajno shraniti v napravi"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Izbira konfiguracije USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Izbira konfiguracije USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Dovoli lažne lokacije"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Dovoli lažne lokacije"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Omogoči pregled atributa pogleda"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Uporaba odjemalca DHCP za Lollipop namesto novega odjemalca DHCP za Android."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Prenos podatkov v mobilnih omrežjih je vedno aktiven – tudi ko je aktivna povezava Wi-Fi (za hiter preklop med omrežji)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Ali dovolite odpravljanje težav s povezavo USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Odpravljanje težav s povezavo USB je namenjeno samo za razvoj. Lahko ga uporabljate za kopiranje podatkov med računalnikom in napravo, nameščanje aplikacij v napravo brez obveščanja in branje podatkov v dnevniku."</string>
diff --git a/packages/SettingsLib/res/values-sq-rAL/arrays.xml b/packages/SettingsLib/res/values-sq-rAL/arrays.xml
index 0f0efb8..c40ad83 100644
--- a/packages/SettingsLib/res/values-sq-rAL/arrays.xml
+++ b/packages/SettingsLib/res/values-sq-rAL/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 milionë/memorie regjistrimi"</item>
<item msgid="5431354956856655120">"16 milionë/memorie regjistrimi"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Joaktive"</item>
+ <item msgid="3054662377365844197">"Të gjitha"</item>
+ <item msgid="688870735111627832">"Të gjitha përveç atyre radio"</item>
+ <item msgid="2850427388488887328">"vetëm bërthama"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Joaktive"</item>
+ <item msgid="172978079776521897">"Të gjitha memoriet e regjistrit"</item>
+ <item msgid="3873873912383879240">"Të gjitha memoriet e regjistrit, përveç atyre radio"</item>
+ <item msgid="8489661142527693381">"vetëm memoria e regjistrimit të bërthamës"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animacioni është i çaktivizuar"</item>
<item msgid="6624864048416710414">"Shkalla e animacionit 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-sq-rAL/strings.xml b/packages/SettingsLib/res/values-sq-rAL/strings.xml
index 4ab5025..e4f0eaa 100644
--- a/packages/SettingsLib/res/values-sq-rAL/strings.xml
+++ b/packages/SettingsLib/res/values-sq-rAL/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Lejo/Ndalo skanimet për Wi‑Fi në roaming, bazuar në sasinë e trafikut të të dhënave të pranishme në ndërfaqe"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Madhësitë e regjistruesit"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Përzgjidh madhësitë e regjistruesit"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Do të pastrosh hapësirën ruajtëse të vazhdueshme të regjistruesit?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Kur nuk monitorojmë më me regjistruesin e vazhdueshëm, kërkohet që t\'i spastrojmë të dhënat e regjistruesit që qëndrojnë në pajisjen tënde."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Ruaj të dhënat e regjistruesit vazhdimisht në pajisje"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Zgjidh memoriet e regjistrit për të ruajtur vazhdimisht në pajisje"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Konfigurimi i USB-së"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Konfigurimi i USB-së"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Lejo vendndodhje të simuluara"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Lejo vendndodhje të simuluara"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Aktivizo shikimin e inspektimit të atributeve"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Përdor klientin DHCP nga Lollipop në vend të klientit të ri DHCP të Androidit."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Mbaji të dhënat celulare gjithmonë aktive edhe kur Wi‑Fi është aktiv (për ndërrim të shpejtë të rrjetit)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Të lejohet korrigjimi i USB-së?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Korrigjuesi i USB-së është vetëm për qëllime zhvillimore. Përdore për të kopjuar të dhëna mes kompjuterit dhe pajisjes tënde, për të instaluar aplikacione në pajisjen tënde pa asnjë njoftim si dhe për të lexuar të dhënat e ditarit."</string>
diff --git a/packages/SettingsLib/res/values-sr/arrays.xml b/packages/SettingsLib/res/values-sr/arrays.xml
index 2389339..1817558 100644
--- a/packages/SettingsLib/res/values-sr/arrays.xml
+++ b/packages/SettingsLib/res/values-sr/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 MB по међумеморији евиденције"</item>
<item msgid="5431354956856655120">"16 MB по међумеморији евиденције"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Искључено"</item>
+ <item msgid="3054662377365844197">"Све"</item>
+ <item msgid="688870735111627832">"Све сeм радија"</item>
+ <item msgid="2850427388488887328">"само језгро"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Искључено"</item>
+ <item msgid="172978079776521897">"Све међумеморије евиденција"</item>
+ <item msgid="3873873912383879240">"Све осим међумеморија евиденција за радио"</item>
+ <item msgid="8489661142527693381">"само међумеморија евиденције језгра"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Анимација је искључена"</item>
<item msgid="6624864048416710414">"Размера анимације 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 9858580..fba58c5 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Дозволи/забрани скенирање Wi-Fi-ја у ромингу на основу присутног протока података на интерфејсу"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Величине бафера података у програму за евидентирање"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Изаберите величине по баферу евиденције"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Желите ли да обришете стални меморијски простор програма за евидентирање?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Када их више не надгледамо помоћу сталног програма за евидентирање, дужни смо да обришемо податке из програма за евидентирање који су трајно смештени на уређају."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Чувај евидентиране податке на уређају"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Изаберите међумеморије евиденције које ћете стално чувати на уређају."</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Изаберите конфигурацију USB-а"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Изаберите конфигурацију USB-а"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Дозволи лажне локације"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Дозволи лажне локације"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Омогући проверу атрибута за преглед"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Користите DHCP клијент из Lollipop-а уместо новог Android DHCP клијента."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Нека подаци за мобилне уређаје увек буду активни, чак и када је Wi‑Fi активан (ради брзе промене мреже)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Дозволи отклањање USB грешака?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Отклањање USB грешака намењено је само за сврхе програмирања. Користите га за копирање података са рачунара на уређај и обрнуто, инсталирање апликација на уређају без обавештења и читање података из евиденције."</string>
diff --git a/packages/SettingsLib/res/values-sv/arrays.xml b/packages/SettingsLib/res/values-sv/arrays.xml
index cbc7dde..9ecedca 100644
--- a/packages/SettingsLib/res/values-sv/arrays.xml
+++ b/packages/SettingsLib/res/values-sv/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 MB/loggbuffert"</item>
<item msgid="5431354956856655120">"16 MB/loggbuffert"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Av"</item>
+ <item msgid="3054662377365844197">"Alla"</item>
+ <item msgid="688870735111627832">"Alla utom radio"</item>
+ <item msgid="2850427388488887328">"endast kernel"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Av"</item>
+ <item msgid="172978079776521897">"Alla loggbuffertar"</item>
+ <item msgid="3873873912383879240">"Alla loggbuffertar utom för radio"</item>
+ <item msgid="8489661142527693381">"endast buffert av kernellogg"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animering avstängd"</item>
<item msgid="6624864048416710414">"Animering i skala 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 45b1b6c..ecc728a 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Tillåt/tillåt inte sökning efter Wi-Fi-roaming utifrån mängden datatrafik i gränssnittet"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Buffertstorlekar för logg"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Välj loggstorlekar per loggbuffert"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Vill du rensa lagringsutrymmet för loggar?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"När övervakningen inte längre görs med permanent loggning måste loggdata som finns på enheten raderas."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Spara logg permanent på enheten"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Välj vilka loggbuffertar som ska sparas permanent på enheten"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Välj USB-konfiguration"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Välj USB-konfiguration"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Tillåt skenplatser"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Tillåt skenplatser"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Aktivera inspektion av visningsattribut"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Använd DHCP-klienten från Lollipop i stället för den nya Android DHCP-klienten."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Håll alltid mobildata aktiverad, även när Wi-Fi är aktiverat (så att du snabbt kan byta mellan nätverk)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Ska USB-felsökning tillåtas?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB-felsökning ska endast användas i utvecklingssyfte. Använd den för att kopiera data mellan datorn och enheten, installera appar på enheten utan meddelanden och läsa loggdata."</string>
diff --git a/packages/SettingsLib/res/values-sw/arrays.xml b/packages/SettingsLib/res/values-sw/arrays.xml
index 8593fd5..e2fbfc3 100644
--- a/packages/SettingsLib/res/values-sw/arrays.xml
+++ b/packages/SettingsLib/res/values-sw/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"M4 kwa kila akiba ya kumbukumbu"</item>
<item msgid="5431354956856655120">"M16 kwa kila akiba ya kumbukumbu"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Yamezimwa"</item>
+ <item msgid="3054662377365844197">"Zote"</item>
+ <item msgid="688870735111627832">"Zote isipokuwa redio"</item>
+ <item msgid="2850427388488887328">"keneli pekee"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Imezimwa"</item>
+ <item msgid="172978079776521897">"Akiba ya kumbukumbu zote"</item>
+ <item msgid="3873873912383879240">"Zote isipokuwa akiba ya kumbukumbu za redio"</item>
+ <item msgid="8489661142527693381">"akiba ya kumbukumbu ya keneli pekee"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Haiwani imezimwa"</item>
<item msgid="6624864048416710414">"Skeli .5x ya haiwani"</item>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 89545f9..435a1bd 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Ruhusu au Zuia Uchanganuzi wa Matumizi ya Mitandao mingine ya Wifi kulingana na kiasi cha trafiki ya data kilicho kwenye kiolesura"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Ukubwa wa kiweka bafa ya kumbukumbu"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Chagua ukubwa wa kila Kumbukumbu"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Ungependa kufuta data iliyo kwenye hifadhi ya kiweka kumbukumbu za mara kwa mara?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Tunapoacha kufuatilia kwa kutumia kiweka kumbukumbu za mara kwa mara, tunapaswa kufuta data yote ya kiweka kumbukumbu iliyo kwenye kifaa chako."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Hifadhi data ya kiweka kumbukumbu kwenye kifaa mara kwa mara"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Chagua akiba za kumbukumbu ili uhifadhi data kwenye kifaa mara kwa mara"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Chagua Usanidi wa USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Chagua Usanidi wa USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Ruhusu maeneo ya jaribio"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Ruhusu maeneo ya majaribio"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Washa ukaguzi wa sifa ya onyesho"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Tumia kiteja cha DHCP kutoka Lollipop badala ya kiteja kipya cha DHCP cha Android."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Washa kila wakati data ya kifaa cha mkononi, hata kama Wi-Fi inatumika (katika uzimaji wa haraka wa mtandao)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Ruhusu utatuaji USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Ueuaji wa USB umekusudiwa kwa malengo ya utengenezaji tu. Itumi kunakili data kati ya kompyuta yako na kifaa chako, kusanidi programu kwa kifaa chako bila arifa, na kusoma data ya rajisi."</string>
diff --git a/packages/SettingsLib/res/values-ta-rIN/arrays.xml b/packages/SettingsLib/res/values-ta-rIN/arrays.xml
index 18deff3..fed3cd1 100644
--- a/packages/SettingsLib/res/values-ta-rIN/arrays.xml
+++ b/packages/SettingsLib/res/values-ta-rIN/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4M / லாக் பஃபர்"</item>
<item msgid="5431354956856655120">"16M / லாக் பஃபர்"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"முடக்கத்தில்"</item>
+ <item msgid="3054662377365844197">"எல்லாம்"</item>
+ <item msgid="688870735111627832">"எல்லாம் (ரேடியோ தவிர்த்து)"</item>
+ <item msgid="2850427388488887328">"கெர்னல் மட்டும்"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"முடக்கத்தில்"</item>
+ <item msgid="172978079776521897">"தற்காலிகமாகச் சேமித்த எல்லா பதிவுகளும்"</item>
+ <item msgid="3873873912383879240">"எல்லாம் (தற்காலிகமாகச் சேமித்த ரேடியோ பதிவுகள் தவிர்த்து)"</item>
+ <item msgid="8489661142527693381">"கெர்னல் லாக் பஃபர் மட்டும்"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"அனிமேஷனை முடக்கு"</item>
<item msgid="6624864048416710414">"அனிமேஷன் அளவு .5x"</item>
diff --git a/packages/SettingsLib/res/values-ta-rIN/strings.xml b/packages/SettingsLib/res/values-ta-rIN/strings.xml
index 75a69a5..033955c 100644
--- a/packages/SettingsLib/res/values-ta-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ta-rIN/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"இடைமுகத்தில் உள்ள ட்ராஃபிக் தரவின் அளவைப் பொறுத்து வைஃபை ரோமிங் ஸ்கேன்களை அனுமதி/அனுமதிக்காதே"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"லாகர் பஃபர் அளவுகள்"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"லாக் பஃபர் ஒன்றிற்கு லாகர் அளவுகளைத் தேர்வுசெய்க"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"லாகரின் நிலையான சேமிப்பகத்தை அழிக்கவா?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"இனி நிலையான லாகர் மூலம் நாங்கள் கண்காணிக்க முடியாத நிலை ஏற்படும் போது, உங்கள் சாதனத்தில் உள்ள லாகர் தரவை நாங்கள் அழிக்க வேண்டி இருக்கும்."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"சாதனத்தில் தொடர்ந்து லாகர் தரவைச் சேமி"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"தொடர்ந்து சாதனத்தில் தற்காலிகமாகச் சேமிக்க வேண்டிய பதிவுகளைத் தேர்ந்தெடுக்கவும்"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB உள்ளமைவைத் தேர்ந்தெடுக்கவும்"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB உள்ளமைவைத் தேர்ந்தெடுக்கவும்"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"போலி இருப்பிடங்களை அனுமதி"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"போலி இருப்பிடங்களை அனுமதி"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"காட்சி பண்புக்கூறு சோதனையை இயக்கு"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"புதிய Android DHCP க்ளையன்ட்டிற்குப் பதிலாக, Lollipop இலிருந்து DHCP க்ளையன்ட்டைப் பயன்படுத்தவும்."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"வைஃபை இயங்கும் போதும் (வேகமான நெட்வொர்க் மாற்றத்திற்கு), மொபைல் தரவை எப்போதும் இயக்கத்தில் வைக்கும்."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB பிழைத்திருத்தத்தை அனுமதிக்கவா?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB பிழைத்திருத்தம் மேம்படுத்தல் நோக்கங்களுக்காக மட்டுமே. அதை உங்கள் கணினி மற்றும் சாதனத்திற்கு இடையில் தரவை நகலெடுக்கவும், அறிவிப்பு இல்லாமல் உங்கள் சாதனத்தில் பயன்பாடுகளை நிறுவவும், பதிவு தரவைப் படிக்கவும் பயன்படுத்தவும்."</string>
diff --git a/packages/SettingsLib/res/values-te-rIN/arrays.xml b/packages/SettingsLib/res/values-te-rIN/arrays.xml
index 3ba0dc7..482a1da 100644
--- a/packages/SettingsLib/res/values-te-rIN/arrays.xml
+++ b/packages/SettingsLib/res/values-te-rIN/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"లాగ్ బఫర్కి 4M"</item>
<item msgid="5431354956856655120">"లాగ్ బఫర్కి 16M"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"ఆఫ్ చేయి"</item>
+ <item msgid="3054662377365844197">"అన్నీ"</item>
+ <item msgid="688870735111627832">"అన్నీ కానీ రేడియో"</item>
+ <item msgid="2850427388488887328">"కెర్నెల్ మాత్రమే"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"ఆఫ్ చేయి"</item>
+ <item msgid="172978079776521897">"అన్ని లాగ్ బఫర్లు"</item>
+ <item msgid="3873873912383879240">"అన్నీ కానీ రేడియో లాగ్ బఫర్లు"</item>
+ <item msgid="8489661142527693381">"కెర్నెల్ లాగ్ బఫర్ మాత్రమే"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"యానిమేషన్ ఆఫ్లో ఉంది"</item>
<item msgid="6624864048416710414">"యానిమేషన్ ప్రమాణం .5x"</item>
diff --git a/packages/SettingsLib/res/values-te-rIN/strings.xml b/packages/SettingsLib/res/values-te-rIN/strings.xml
index 5a7a7ae..5758176 100644
--- a/packages/SettingsLib/res/values-te-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-te-rIN/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"ఇంటర్ఫేస్లో ఉండే డేటా ట్రాఫిక్ పరిమాణం ఆధారంగా Wi‑Fi సంచార స్కాన్లను అనుమతించు/నిరాకరించు"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"లాగర్ బఫర్ పరిమాణాలు"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"లాగ్ బఫర్కి లాగర్ పరిమా. ఎంచుకోండి"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"లాగర్ నిరంతర నిల్వలోని డేటాను తీసివేయాలా?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"మేము నిరంతర లాగర్తో ఇక పర్యవేక్షించనప్పుడు, మీ పరికరంలోని లాగర్ డేటాను మేము తొలగించాల్సి ఉంటుంది."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"పరికరంలో లాగర్ డేటా నిరంతరం నిల్వ చేయి"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"పరికరంలో నిరంతరం నిల్వ చేయాల్సిన లాగ్ బఫర్లను ఎంచుకోండి"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB కాన్ఫిగరేషన్ని ఎంచుకోండి"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB కాన్ఫిగరేషన్ని ఎంచుకోండి"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"అనుకృత స్థానాలను అనుమతించు"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"అనుకృత స్థానాలను అనుమతించు"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"వీక్షణ లక్షణ పర్యవేక్షణను ప్రారంభించు"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"కొత్త Android DHCP క్లయింట్కి బదులుగా Lollipop నుండి DHCP క్లయింట్ను ఉపయోగించండి."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"ఎల్లప్పుడూ మొబైల్ డేటాను సక్రియంగా ఉంచు, Wi‑Fi సక్రియంగా ఉన్నా కూడా (వేగవంతమైన నెట్వర్క్ మార్పు కోసం)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB డీబగ్గింగ్ను అనుమతించాలా?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB డీబగ్గింగ్ అనేది అభివృద్ధి ప్రయోజనాల కోసం మాత్రమే ఉద్దేశించబడింది. మీ కంప్యూటర్ మరియు మీ పరికరం మధ్య డేటాను కాపీ చేయడానికి, నోటిఫికేషన్ లేకుండా మీ పరికరంలో అనువర్తనాలను ఇన్స్టాల్ చేయడానికి మరియు లాగ్ డేటాను చదవడానికి దీన్ని ఉపయోగించండి."</string>
diff --git a/packages/SettingsLib/res/values-th/arrays.xml b/packages/SettingsLib/res/values-th/arrays.xml
index 4282975..08caeb6 100644
--- a/packages/SettingsLib/res/values-th/arrays.xml
+++ b/packages/SettingsLib/res/values-th/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4 M ต่อบัฟเฟอร์ไฟล์บันทึก"</item>
<item msgid="5431354956856655120">"16 M ต่อบัฟเฟอร์ไฟล์บันทึก"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"ปิด"</item>
+ <item msgid="3054662377365844197">"ทั้งหมด"</item>
+ <item msgid="688870735111627832">"ทั้งหมดเว้นวิทยุ"</item>
+ <item msgid="2850427388488887328">"เฉพาะเคอร์เนล"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"ปิด"</item>
+ <item msgid="172978079776521897">"บัฟเฟอร์บันทึกทั้งหมด"</item>
+ <item msgid="3873873912383879240">"ทั้งหมดยกเว้นบัฟเฟอร์บันทึกวิทยุ"</item>
+ <item msgid="8489661142527693381">"เฉพาะบัฟเฟอร์ไฟล์บันทึกเคอร์เนล"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"ปิดภาพเคลื่อนไหว"</item>
<item msgid="6624864048416710414">"ภาพเคลื่อนไหวขนาด 0.5 เท่า"</item>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index d7fe2b6..4849e1955 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"อนุญาต/ไม่อนุญาตการสแกน Wi-Fi ข้ามเครือข่าย ตามปริมาณข้อมูลการเข้าชมที่ปรากฏในอินเทอร์เฟซ"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"ขนาดบัฟเฟอร์ของ Logger"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"เลือกขนาด Logger ต่อบัฟเฟอร์ไฟล์บันทึก"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"ล้างพื้นที่เก็บข้อมูลถาวรของตัวบันทึกไหม"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"เมื่อเราเลิกตรวจสอบด้วยตัวบันทึกถาวร เราต้องลบ Resident ของข้อมูลตัวบันทึกบนอุปกรณ์ของคุณ"</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"เก็บข้อมูลตัวบันทึกอย่างถาวรบนอุปกรณ์"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"เลือกบัฟเฟอร์บันทึกที่จะจัดเก็บอย่างถาวรบนอุปกรณ์"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"เลือกการกำหนดค่า USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"เลือกการกำหนดค่า USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"อนุญาตให้จำลองตำแหน่ง"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"อนุญาตให้จำลองตำแหน่ง"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"เปิดใช้การตรวจสอบแอตทริบิวต์มุมมอง"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"ใช้ไคลเอ็นต์ DHCP จาก Lollipop แทนไคลเอ็นต์ DHCP ใหม่บน Android"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"เปิดใช้ข้อมูลมือถืออยู่เสมอ แม้ในเวลาที่ใช้งาน Wi-Fi อยู่ (สำหรับสวิตชิงเครือข่ายความเร็วสูง)"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"อนุญาตให้แก้ไขข้อบกพร่อง USB หรือไม่"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"การแก้ไขข้อบกพร่อง USB มีไว้เพื่อการพัฒนาเท่านั้น ให้ใช้การแก้ไขนี้เพื่อคัดลอกข้อมูลระหว่างคอมพิวเตอร์และอุปกรณ์ ติดตั้งแอปพลิเคชันบนอุปกรณ์โดยไม่มีการแจ้งเตือน และอ่านข้อมูลบันทึก"</string>
diff --git a/packages/SettingsLib/res/values-tl/arrays.xml b/packages/SettingsLib/res/values-tl/arrays.xml
index a7fb68c..a1505dc 100644
--- a/packages/SettingsLib/res/values-tl/arrays.xml
+++ b/packages/SettingsLib/res/values-tl/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4M kada log buffer"</item>
<item msgid="5431354956856655120">"16M kada log buffer"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Naka-off"</item>
+ <item msgid="3054662377365844197">"Lahat"</item>
+ <item msgid="688870735111627832">"Maliban sa radyo"</item>
+ <item msgid="2850427388488887328">"kernel lang"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Naka-off"</item>
+ <item msgid="172978079776521897">"Lahat ng buffer ng log"</item>
+ <item msgid="3873873912383879240">"Lahat maliban sa buffer ng log ng radyo"</item>
+ <item msgid="8489661142527693381">"kernel log buffer lang"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Naka-off ang animation"</item>
<item msgid="6624864048416710414">"Scale ng animation .5x"</item>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 2cd6813..24f5499 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Payagan/Huwag payagan ang Mga Pag-scan sa Roaming ng Wi‑Fi batay sa dami ng trapiko ng data na mayroon sa interface"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Mga laki ng buffer ng Logger"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Pumili ng mga laki ng Logger bawat log buffer"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"I-clear ang tuluy-tuloy na storage ng logger?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Kapag hindi na namin sinusubaybayan ang tuluy-tuloy na logger, kailangan naming burahin ang data ng logger na nanatili sa iyong device."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Tuluy-tuloy na iimbak ang data ng logger sa device"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Pumili ng mga buffer ng log upang tuluy-tuloy na iimbak sa device"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Pumili ng USB Configuration"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Pumili ng USB Configuration"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Payagan ang mga kunwaring lokasyon"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Payagan ang mga kunwaring lokasyon"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"I-enable ang pagsisiyasat sa attribute na view"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Gamitin ang DHCP client mula sa Lollipop sa halip na ang bagong Android DHCP client."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Palaging panatilihing aktibo ang mobile data, kahit na aktibo ang Wi‑Fi (para sa mabilis na paglipat ng network)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Payagan ang pag-debug ng USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Ang pag-debug ng USB ay nilalayon para sa mga layuning pagpapabuti lamang. Gamitin ito upang kumopya ng data sa pagitan ng iyong computer at iyong device, mag-install ng apps sa iyong device nang walang notification, at magbasa ng data ng log."</string>
diff --git a/packages/SettingsLib/res/values-tr/arrays.xml b/packages/SettingsLib/res/values-tr/arrays.xml
index 0bae437..c97e79c 100644
--- a/packages/SettingsLib/res/values-tr/arrays.xml
+++ b/packages/SettingsLib/res/values-tr/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"Günlük arabelleği başına 4 MB"</item>
<item msgid="5431354956856655120">"Günlük arabelleği başına 16 MB"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Kapalı"</item>
+ <item msgid="3054662377365844197">"Tümü"</item>
+ <item msgid="688870735111627832">"Radyo hariç tümü"</item>
+ <item msgid="2850427388488887328">"yaln. çekirdek"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Kapalı"</item>
+ <item msgid="172978079776521897">"Günlük arabelleklerin tümü"</item>
+ <item msgid="3873873912383879240">"Radyo günlük arabellekleri hariç tümü"</item>
+ <item msgid="8489661142527693381">"yalnızca çekirdek günlük arabelleği"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animasyon kapalı"</item>
<item msgid="6624864048416710414">"Animasyon ölçeği 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index ceb1292..950e322 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Arayüzde mevcut veri trafiği miktarına bağlı olarak Kablosuz Dolaşım Taramalarına İzin Verin/Vermeyin"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Günlük Kaydedici arabellek boyutları"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Gün. arabel. başına Gün. Kayd. boyutunu seç"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Günlük kaydedici kalıcı depolama alanı silinsin mi?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Kalıcı günlük kaydediciyle artık izlemediğimizde, cihazınızda bulunan günlük kaydedici verilerini silmemiz gerekmektedir."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Günlük kaydedici verilerini bu cihazda kalıcı olarak depola"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Cihazda kalıcı olarak depolanacak günlük arabelleklerini seçin"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB Yapılandırmasını seç"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB Yapılandırmasını seç"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Sahte konumlara izin ver"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Sahte konumlara izin ver"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Görünüm özelliği incelemeyi etkinleştir"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Yeni Android DHCP istemcisi yerine Lollipop DHCP istemcisini kullan."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Kablosuz bağlantı etkin bile olsa mobil veri kullanımını her zaman etkin tut (ağlar arasında hızlı geçiş yapmak için)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB hata ayıklamasına izin verilsin mi?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB hata ayıklaması yalnızca geliştirme amaçlıdır. Verileri bilgisayarınızla cihazınız arasında kopyalamak, bildirim göndermeksizin uygulamaları cihazınıza yüklemek ve günlük verilerini okumak için kullanın."</string>
diff --git a/packages/SettingsLib/res/values-uk/arrays.xml b/packages/SettingsLib/res/values-uk/arrays.xml
index 0786ac3..7b720f3 100644
--- a/packages/SettingsLib/res/values-uk/arrays.xml
+++ b/packages/SettingsLib/res/values-uk/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"Буфер журналу: 4 Мб"</item>
<item msgid="5431354956856655120">"Буфер журналу: 16 Мб"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Вимкнено"</item>
+ <item msgid="3054662377365844197">"Усі"</item>
+ <item msgid="688870735111627832">"Усі, крім радіо"</item>
+ <item msgid="2850427388488887328">"лише ядро"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Вимкнено"</item>
+ <item msgid="172978079776521897">"Буфери всіх журналів"</item>
+ <item msgid="3873873912383879240">"Буфери всіх журналів, крім радіо"</item>
+ <item msgid="8489661142527693381">"лише буфер журналу ядра"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Анімацію вимкнено"</item>
<item msgid="6624864048416710414">"Анімація 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 44c9be7..6b49b14 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Дозволити чи заборонити Wi-Fi шукати роумінг на основі обсягу трафіку даних в інтерфейсі"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Розміри буфера журналу"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Виберіть розміри буфера журналу"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Очистити постійну пам’ять журналу?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Коли постійний журнал більше не відстежується, ми маємо видалити його дані з вашого пристрою."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Постійно зберігати дані журналів"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Виберіть буфери журналів, які мають постійно зберігатися на пристрої"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Вибрати конфігурацію USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Вибрати конфігурацію USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Фіктивні місцезнаходження"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Дозв. фіктивні місцезн."</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Увімкнути оцінку атрибуції переглядів"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Використовувати клієнт DHCP з Lollipop, а не новий клієнт DHCP з Android."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Не вимикати мобільний Інтернет, навіть якщо ввімкнено Wi‑Fi (щоб швидше переходити між мережами)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Дозвол. налагодж. USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Налагодження USB застосовується лише з метою розробки. Його можна використовувати для копіювання даних між комп’ютером і пристроєм, встановлення програм на вашому пристрої без сповіщення та читання даних журналу."</string>
diff --git a/packages/SettingsLib/res/values-ur-rPK/arrays.xml b/packages/SettingsLib/res/values-ur-rPK/arrays.xml
index e1fe269..4f081e5 100644
--- a/packages/SettingsLib/res/values-ur-rPK/arrays.xml
+++ b/packages/SettingsLib/res/values-ur-rPK/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4M فی لاگ بفر"</item>
<item msgid="5431354956856655120">"16M فی لاگ بفر"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"آف"</item>
+ <item msgid="3054662377365844197">"تمام"</item>
+ <item msgid="688870735111627832">"ریڈیو کے سوا تمام"</item>
+ <item msgid="2850427388488887328">"صرف کرنل"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"آف"</item>
+ <item msgid="172978079776521897">"تمام لاگ بفرز"</item>
+ <item msgid="3873873912383879240">"ریڈیو لاگ بفرز کے سوا تمام"</item>
+ <item msgid="8489661142527693381">"صرف کرنل لاگ بفر"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"اینیمیشن آف ہے"</item>
<item msgid="6624864048416710414">"اینیمیشن اسکیل .5x"</item>
diff --git a/packages/SettingsLib/res/values-ur-rPK/strings.xml b/packages/SettingsLib/res/values-ur-rPK/strings.xml
index 6490204..2ac8a61 100644
--- a/packages/SettingsLib/res/values-ur-rPK/strings.xml
+++ b/packages/SettingsLib/res/values-ur-rPK/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"انٹرفیس پر موجود ڈیٹا ٹریفک کی مقدار کی بنیاد پر Wi‑Fi روم اسکینز کی اجازت دیں/اجازت نہ دیں"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"لاگر بفر کے سائز"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"فی لاگ بفر لاگر کے سائز منتخب کریں"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"لاگر مستقل اسٹوریج صاف کریں؟"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"جب ہم مستقل لاگر کے ساتھ نگرانی نہیں کر رہے ہوتے تو ہمیں آپ کے آلہ پر موجود لاگر ڈیٹا کو مٹانا ہوتا ہے۔"</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"لاگر ڈیٹا مستقل طور پر آلہ پر اسٹور کریں"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"آلہ پر مستقل طور پر اسٹور کرنے کیلئے لاگ بفرز استعمال کریں"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB کنفیگریشن منتخب کریں"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB کنفیگریشن منتخب کریں"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"فرضی مقامات کی اجازت دیں"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"فرضی مقامات کی اجازت دیں"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"منظر انتساب کے معائنہ کو فعال کریں"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"نئے Android DHCP کلائنٹ کی بجائے Lollipop کا DHCP کلائنٹ استعمال کریں۔"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Wi‑Fi فعال ہونے پر بھی موبائل ڈیٹا کو ہمیشہ فعال رکھیں (تیزی سے نیٹ ورک سوئچ کرنے کیلئے)۔"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB ڈیبگ کرنے کی اجازت دیں؟"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB ڈیبگ کرنا صرف ڈیولپمنٹ کے مقاصد کیلئے ہے۔ اپنے کمپیوٹر اور اپنے آلہ کے درمیان ڈیٹا کاپی کرنے کیلئے اسے استعمال کریں، بغیر اطلاع کے اپنے آلہ پر ایپس انسٹال کریں اور لاگ ڈیٹا پڑھیں۔"</string>
diff --git a/packages/SettingsLib/res/values-uz-rUZ/arrays.xml b/packages/SettingsLib/res/values-uz-rUZ/arrays.xml
index 53d4db7..f501242 100644
--- a/packages/SettingsLib/res/values-uz-rUZ/arrays.xml
+++ b/packages/SettingsLib/res/values-uz-rUZ/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"Bufer: maks. 4 MB"</item>
<item msgid="5431354956856655120">"Bufer: maks. 16 MB"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"O‘chiq"</item>
+ <item msgid="3054662377365844197">"Hammasi"</item>
+ <item msgid="688870735111627832">"Radiodan boshqa hammasi"</item>
+ <item msgid="2850427388488887328">"faqat yadro"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"O‘chiq"</item>
+ <item msgid="172978079776521897">"Barcha jurnallar buferi"</item>
+ <item msgid="3873873912383879240">"Radio jurnallar buferidan tashqari hammasi"</item>
+ <item msgid="8489661142527693381">"faqat yadro jurnali buferi"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animatsiya o‘chiq"</item>
<item msgid="6624864048416710414">"Animatsiya (0,5x)"</item>
diff --git a/packages/SettingsLib/res/values-uz-rUZ/strings.xml b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
index bd0cc8c..9022ad3 100644
--- a/packages/SettingsLib/res/values-uz-rUZ/strings.xml
+++ b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
@@ -75,7 +75,7 @@
<string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Agar ulanishga ruxsat bersangiz, ulangan vaqtda kontakt va qo‘ng‘iroqlaringiz tarixiga kirishi mumkin."</string>
<string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> bilan biriktirib bo‘lmadi."</string>
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> qurilmasiga ulanib bo‘lmadi, chunki PIN-kod yoki parol noto‘g‘ri kiritildi."</string>
- <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Quyidagi qurilma javob bermayapti: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"“<xliff:g id="DEVICE_NAME">%1$s</xliff:g>” qurilmasi bilan aloqa o‘rnatib bo‘lmayapti."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> biriktirish so‘rovini rad qildi."</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi o‘chiq."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi o‘chiq."</string>
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Ma’lumotlarni uzatish vaqtida trafik hajmiga qarab Wi-Fi tarmoqlarni qidirish funksiyasini yoqish yoki o‘chirish"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Jurnal buferi hajmi"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Jurnal xotirasi hajmini tanlang"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Jurnalning doimiy xotirasi tozalansinmi?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Qachon doimiy jurnal nazorat qilinmasa, uning ma’lumotlarini qurilmadan o‘chirib tashlanadi."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Jurnal ma’lumotlari doim saqlansin"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Xotirada doimiy saqlanishi kerak bo‘lgan jurnal buferini tanlang"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"USB konfiguratsiyasini tanlang"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"USB konfiguratsiyasini tanlang"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Qo‘lbola joylashuvlarga ruxsat berish"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Joylashuv emulyatsiyasiga ruxsat berish"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Alomatlar tekshiruvini yoqish"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Yangi Android DHCP mijoz-dasturi o‘rniga Lollipop tizimi DHCP mijoz-dasturidan foydalanilsin."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Mobil internet har doim yoniq tursin, hatto Wi-Fi yoniq bo‘lsa ham (bir tarmoqdan ikkinchisiga tezroq o‘tish uchun)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB orqali nosozliklarni tuzatishga ruxsat berilsinmi?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB orqali nosozliklarni tuzatish faqat dasturlash maqsadlarida yoqiladi. Undan ma‘lumotlarni qurilmangiz va kompyuter o‘rtasida ko‘chirish, ilovalarni xabarnomasiz o‘rnatish va jurnal ma‘lumotlarini o‘qish uchun foydalaniladi."</string>
diff --git a/packages/SettingsLib/res/values-vi/arrays.xml b/packages/SettingsLib/res/values-vi/arrays.xml
index b03d847..237b4f4 100644
--- a/packages/SettingsLib/res/values-vi/arrays.xml
+++ b/packages/SettingsLib/res/values-vi/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4M/lần tải nhật ký"</item>
<item msgid="5431354956856655120">"16M/lần tải nhật ký"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Tắt"</item>
+ <item msgid="3054662377365844197">"Tất cả"</item>
+ <item msgid="688870735111627832">"Tất cả trừ đài"</item>
+ <item msgid="2850427388488887328">"chỉ kernel"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Tắt"</item>
+ <item msgid="172978079776521897">"Tất cả lần tải nhật ký"</item>
+ <item msgid="3873873912383879240">"Tất cả trừ lần tải nhật ký qua đài"</item>
+ <item msgid="8489661142527693381">"chỉ vùng đệm nhật ký kernel"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Tắt hình động"</item>
<item msgid="6624864048416710414">"Tỷ lệ hình động 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index c6b47fd..c58f849 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Cho phép/Không cho phép quét chuyển vùng Wi‑Fi dựa trên lưu lượng truy cập dữ liệu có tại giao diện"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Kích cỡ tải trình ghi"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Chọn kích thước Trình ghi/lần tải nhật ký"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Xóa bộ nhớ ổn định trong trình ghi nhật ký?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Khi chúng tôi không còn theo dõi bằng trình ghi nhật ký ổn định nữa, chúng tôi sẽ được yêu cầu xóa dữ liệu trong trình ghi nhật ký nằm trên thiết bị của bạn."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Lưu dữ liệu trình ghi nhật ký ổn định"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Chọn lần tải nhật ký để lưu trữ ổn định trên thiết bị"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Chọn cấu hình USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Chọn cấu hình USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Cho phép vị trí mô phỏng"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Cho phép vị trí mô phỏng"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Cho phép kiểm tra thuộc tính của chế độ xem"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Sử dụng ứng dụng DHCP từ Lollipop thay vì ứng dụng DHCP mới của Android."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Luôn giữ cho dữ liệu di động hoạt động, ngay cả khi Wi-Fi đang hoạt động (để chuyển đổi mạng nhanh)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Cho phép gỡ lỗi USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Gỡ lỗi USB chỉ dành cho mục đích phát triển. Hãy sử dụng tính năng này để sao chép dữ liệu giữa máy tính và thiết bị của bạn, cài đặt ứng dụng trên thiết bị của bạn mà không thông báo và đọc dữ liệu nhật ký."</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/arrays.xml b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
index d1d8937..8a3febd 100644
--- a/packages/SettingsLib/res/values-zh-rCN/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"每个日志缓冲区 4M"</item>
<item msgid="5431354956856655120">"每个日志缓冲区 16M"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"关闭"</item>
+ <item msgid="3054662377365844197">"全部"</item>
+ <item msgid="688870735111627832">"所有非无线电"</item>
+ <item msgid="2850427388488887328">"仅限内核"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"关闭"</item>
+ <item msgid="172978079776521897">"所有日志缓冲区"</item>
+ <item msgid="3873873912383879240">"所有非无线电日志缓冲区"</item>
+ <item msgid="8489661142527693381">"仅限内核日志缓冲区"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"关闭动画"</item>
<item msgid="6624864048416710414">"动画缩放 0.5x"</item>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 6b4c453..18ae80e 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -132,8 +132,8 @@
<item msgid="164347302621392996">"快"</item>
<item msgid="5794028588101562009">"较快"</item>
<item msgid="7163942783888652942">"非常快"</item>
- <item msgid="7831712693748700507">"迅速"</item>
- <item msgid="5194774745031751806">"很迅速"</item>
+ <item msgid="7831712693748700507">"超快"</item>
+ <item msgid="5194774745031751806">"极快"</item>
<item msgid="9085102246155045744">"最快"</item>
</string-array>
<string name="choose_profile" msgid="8229363046053568878">"选择个人资料"</string>
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"根据接口中目前的数据流量允许/禁止WLAN漫游扫描"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"日志记录器缓冲区大小"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"选择每个日志缓冲区的日志记录器大小"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"要清除永久存储的日志记录器数据吗?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"当我们不再使用永久日志记录器进行监控时,我们需要清除保存在您设备上的日志记录器数据。"</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"在设备上永久存储日志记录器数据"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"选择要在设备上永久存储的日志缓冲区"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"选择USB配置"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"选择USB配置"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"允许模拟位置"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"允许模拟位置"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"启用视图属性检查功能"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"使用 Lollipop 的 DHCP 客户端,而不是新的 Android DHCP 客户端。"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"始终开启移动数据网络,即使 WLAN 网络已开启(便于快速切换网络)。"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"是否允许USB调试?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB调试仅适用于开发工作。该功能可用于在您的计算机和设备之间复制数据、在您的设备上安装应用而不发送通知以及读取日志数据。"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/arrays.xml b/packages/SettingsLib/res/values-zh-rHK/arrays.xml
index a7b0031..fe65884 100644
--- a/packages/SettingsLib/res/values-zh-rHK/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"每個記錄緩衝區 4M"</item>
<item msgid="5431354956856655120">"每個記錄緩衝區 16M"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"關閉"</item>
+ <item msgid="3054662377365844197">"全部"</item>
+ <item msgid="688870735111627832">"所有非無線電"</item>
+ <item msgid="2850427388488887328">"只限核心"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"關閉"</item>
+ <item msgid="172978079776521897">"所有記錄緩衝區"</item>
+ <item msgid="3873873912383879240">"所有非無線電記錄緩衝區"</item>
+ <item msgid="8489661142527693381">"只限核心記錄緩衝區"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"關閉動畫"</item>
<item msgid="6624864048416710414">"動畫比例 .5x"</item>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index c8cb2d9..660071d 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"根據介面中目前的數據流量允許/禁止 WiFi 漫遊掃瞄"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"記錄器緩衝區空間"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"選取每個記錄緩衝區的記錄器空間"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"要清除記錄器的持久儲存空間嗎?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"當我們不再使用持久記錄器進行監察,便需要清除您裝置上的記錄器資料。"</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"在裝置持久儲存記錄器資料"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"選擇記錄緩衝區,以便將資料持久儲存在裝置中"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"選取 USB 設定"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"選取 USB 設定"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"允許模擬位置"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"允許模擬位置"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"啟用檢視屬性檢查"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"使用 Lollipop 的 DHCP 用戶端,而不是新的 Android DHCP 用戶端。"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"即使 Wi‑Fi 已啟用,仍永遠啟用流動數據 (可快速切換網絡)。"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"允許 USB 偵錯嗎?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB 偵錯是針對應用程式開發而設計的功能,可讓您在電腦與裝置間複製資料、不用通知即可在裝置上安裝應用程式,以及讀取記錄資料。"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/arrays.xml b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
index 32a2065..0939e93 100644
--- a/packages/SettingsLib/res/values-zh-rTW/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"每個紀錄緩衝區 4M"</item>
<item msgid="5431354956856655120">"每個紀錄緩衝區 16M"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"關閉"</item>
+ <item msgid="3054662377365844197">"全部"</item>
+ <item msgid="688870735111627832">"無線電以外"</item>
+ <item msgid="2850427388488887328">"僅限核心"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"關閉"</item>
+ <item msgid="172978079776521897">"所有紀錄緩衝區"</item>
+ <item msgid="3873873912383879240">"無線電紀錄緩衝區以外的所有紀錄緩衝區"</item>
+ <item msgid="8489661142527693381">"僅限核心紀錄緩衝區"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"關閉動畫"</item>
<item msgid="6624864048416710414">"動畫比例 0.5x"</item>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 75f9758..bce6f24 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"根據介面中目前的數據流量允許/禁止 Wi-Fi 漫遊掃描"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"紀錄器緩衝區空間"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"選取每個紀錄緩衝區的紀錄器空間"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"要清除永久儲存的記錄器資料嗎?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"如果您選擇不再透過永久記錄器進行監控,系統就必須清除裝置中的記錄器資料。"</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"在裝置上永久儲存記錄器資料"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"選取要在裝置上永久儲存的紀錄緩衝區"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"選取 USB 設定"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"選取 USB 設定"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"允許模擬位置"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"允許模擬位置"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"啟用檢視屬性檢查"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"使用 Lollipop 的 DHCP 用戶端,不使用新型 Android DHCP 用戶端。"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"即使 Wi‑Fi 連線已啟用,一律將行動數據連線保持啟用狀態 (以便快速切換網路)。"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"允許 USB 偵錯嗎?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB 偵錯是針對應用程式開發而設計的功能,可讓您複製電腦和裝置中的資料、不需經由通知即可在裝置上安裝應用程式,以及讀取記錄資料。"</string>
diff --git a/packages/SettingsLib/res/values-zu/arrays.xml b/packages/SettingsLib/res/values-zu/arrays.xml
index 2bb849f..772dee8 100644
--- a/packages/SettingsLib/res/values-zu/arrays.xml
+++ b/packages/SettingsLib/res/values-zu/arrays.xml
@@ -80,6 +80,18 @@
<item msgid="3606047780792894151">"4M ngebhafa yelogu ngayinye"</item>
<item msgid="5431354956856655120">"16M ngebhafa yelogu ngayinye"</item>
</string-array>
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Valiwe"</item>
+ <item msgid="3054662377365844197">"Konke"</item>
+ <item msgid="688870735111627832">"Konke ngaphandle kwerediyo"</item>
+ <item msgid="2850427388488887328">"i-kernel kuphela"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Valiwe"</item>
+ <item msgid="172978079776521897">"Onke amabhafa elogi"</item>
+ <item msgid="3873873912383879240">"Konke ngaphandle kwamabhafa elogi yerediyo"</item>
+ <item msgid="8489661142527693381">"ilogi ye-kernel kuphela"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Isithombe esinyakazayo sivliwe"</item>
<item msgid="6624864048416710414">"Isilinganiso sesithombe esinyakazayo ngu-05x"</item>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 1217183..07c6fce 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -175,12 +175,15 @@
<string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Vumela/Ungavumeli ukuskena kokuzula kwe-Wi-Fi okususelwa kunani ledatha yethrafikhi ekhona ekusebenzisaneni"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Amasayizi weloga ngebhafa"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Khetha amasayizi weloga ngebhafa ngayinye yelogu"</string>
+ <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Sula isitoreji seloga eqhubekayo?"</string>
+ <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Uma singasaqaphi ngeloga eqhubekayo, kuzomele sisule idatha yeloga ehleli kudivayisi yakho."</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Gcina idatha yeloga eqhubekayo kudivayisi yakho"</string>
+ <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Khetha amabhafa elogi ukuze uwagcine ngokuqhubeka kudivayisi"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Khetha ukulungiselelwa kwe-USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Khetha ukulungiselelwa kwe-USB"</string>
<string name="allow_mock_location" msgid="2787962564578664888">"Vumela izindawo mbumbulu"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Vumela izindawo mbumbulu"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Nika amandla ukubuka"</string>
- <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Sebenzisa iklayenti le-DHCP kusukela ku-Lollipop esikhundleni seklayenti elisha le-Android DHCP."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Hlala ugcine idatha yeselula isebenza, nanoma i-Wi-Fi isebenza (ngokushintshwa kwenethiwekhi okusheshayo)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Vumela ukulungisa iphutha le-USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Ukulungisa iphutha le-USB kuhloselwe izinjongo zokuthuthukisa kuphela. Ingasebenziselwa ukukopisha idatha phakathi kwekhompyutha yakho nedivaysi yakho, faka izinhlelo zokusebenza kwidivaysi yakho ngaphandle kwesaziso, bese ufunda idatha yefayela lokungena."</string>
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 525d6f4..920e061 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -138,6 +138,30 @@
<item>16M per log buffer</item>
</string-array>
+ <!-- Values for logpersist state selection preference. -->
+ <string-array name="select_logpersist_values" translatable="false" >
+ <item></item>
+ <item>all</item>
+ <item>default,security,kernel</item>
+ <item>kernel</item>
+ </string-array>
+
+ <!-- Titles for logpersist state selection preference. [CHAR LIMIT=14] -->
+ <string-array name="select_logpersist_titles">
+ <item>Off</item>
+ <item>All</item>
+ <item>All but radio</item>
+ <item>kernel only</item>
+ </string-array>
+
+ <!-- Summaries for logpersist state selection preference. [CHAR LIMIT=50]-->
+ <string-array name="select_logpersist_summaries" >
+ <item>Off</item>
+ <item>All log buffers</item>
+ <item>All but radio log buffers</item>
+ <item>kernel log buffer only</item>
+ </string-array>
+
<!-- Titles for window animation scale preference. [CHAR LIMIT=35] -->
<string-array name="window_animation_scale_entries">
<item>Animation off</item>
@@ -362,4 +386,12 @@
<item>3</item>
</string-array>
+ <!-- IDs for each color mode. The values must match the corresponding constants in
+ android.view.Display -->
+ <integer-array name="color_mode_ids">
+ <item>0</item>
+ <item>-1</item>
+ <item>7</item>
+ </integer-array>
+
</resources>
diff --git a/packages/SettingsLib/res/values/colors.xml b/packages/SettingsLib/res/values/colors.xml
index 796273d..02b7ea6 100644
--- a/packages/SettingsLib/res/values/colors.xml
+++ b/packages/SettingsLib/res/values/colors.xml
@@ -17,5 +17,5 @@
<resources>
<color name="disabled_text_color">#66000000</color> <!-- 38% black -->
- <color name="usage_graph_dots">#455A64</color>
+ <color name="usage_graph_dots">@*android:color/tertiary_device_default_settings</color>
</resources>
diff --git a/packages/SettingsLib/res/values/config.xml b/packages/SettingsLib/res/values/config.xml
index 299a5b7..0cf4a41 100755
--- a/packages/SettingsLib/res/values/config.xml
+++ b/packages/SettingsLib/res/values/config.xml
@@ -19,4 +19,22 @@
<resources>
<!-- Configuration for automotive -->
<bool name="enable_pbap_pce_profile">false</bool>
+
+ <!-- Default data warning level in mb -->
+ <integer name="default_data_warning_level_mb">2048</integer>
+
+ <!-- Whether to send a custom package name with the PSD. translatable="false"-->
+ <bool name="config_sendPackageName">true</bool>
+
+ <!-- Name for the set of keys associating package names -->
+ <string name="config_helpPackageNameKey" translatable="false"></string>
+
+ <!-- Name for the set of values of package names -->
+ <string name="config_helpPackageNameValue" translatable="false"></string>
+
+ <!-- Intent key for the package name keys -->
+ <string name="config_helpIntentExtraKey" translatable="false"></string>
+
+ <!-- Intent key for package name values -->
+ <string name="config_helpIntentNameKey" translatable="false"></string>
</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 89c46d7..7d42211 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -438,6 +438,14 @@
<string name="select_logd_size_title">Logger buffer sizes</string>
<!-- UI debug setting: limit size of Android logger buffers [CHAR LIMIT=59] -->
<string name="select_logd_size_dialog_title">Select Logger sizes per log buffer</string>
+ <!-- UI debug setting: store logs persistently -->
+ <string name="dev_logpersist_clear_warning_title">Clear logger persistent storage?</string>
+ <!-- Warning text to user about the implications of enabling USB debugging -->
+ <string name="dev_logpersist_clear_warning_message">When we no longer are monitoring with the persistent logger, we are required to erase the logger data resident on your device.</string>
+ <!-- Title of checkbox setting to perform package verification on apps installed over USB/ADT/ADB [CHAR LIMIT=32] -->
+ <string name="select_logpersist_title">Store logger data persistently on device</string>
+ <!-- UI debug setting: select which logs to store persistently [CHAR LIMIT=80] -->
+ <string name="select_logpersist_dialog_title">Select log buffers to store persistently on device</string>
<!-- UI debug setting: select USB configuration -->
<string name="select_usb_configuration_title">Select USB Configuration</string>
<!-- UI debug setting: limit size of Android logger buffers [CHAR LIMIT=59] -->
@@ -448,8 +456,6 @@
<string name="allow_mock_location_summary">Allow mock locations</string>
<!-- Setting Checkbox title whether to enable view attribute inspection -->
<string name="debug_view_attributes">Enable view attribute inspection</string>
- <!-- Setting Checkbox summary whether to use DHCP client from Lollipop (Android 5.0) [CHAR LIMIT=130] -->
- <string name="legacy_dhcp_client_summary">Use the DHCP client from Lollipop instead of the new Android DHCP client.</string>
<string name="mobile_data_always_on_summary">Always keep mobile data active, even when Wi\u2011Fi is active (for fast network switching).</string>
<!-- Title of warning dialog about the implications of enabling USB debugging -->
<string name="adb_warning_title">Allow USB debugging?</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
index ff1c866..b04948b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
@@ -22,6 +22,9 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Build;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionInfo;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.Log;
@@ -37,6 +40,8 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import static android.content.Context.TELEPHONY_SERVICE;
+
public class DeviceInfoUtils {
private static final String TAG = "DeviceInfoUtils";
@@ -169,4 +174,40 @@
}
}
+ public static String getFormattedPhoneNumber(Context context, SubscriptionInfo subscriptionInfo) {
+ String formattedNumber = null;
+ if (subscriptionInfo != null) {
+ final TelephonyManager telephonyManager =
+ (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
+ final String rawNumber =
+ telephonyManager.getLine1Number(subscriptionInfo.getSubscriptionId());
+ if (!TextUtils.isEmpty(rawNumber)) {
+ formattedNumber = PhoneNumberUtils.formatNumber(rawNumber);
+ }
+
+ }
+ return formattedNumber;
+ }
+
+ public static String getFormattedPhoneNumbers(Context context,
+ List<SubscriptionInfo> subscriptionInfo) {
+ StringBuilder sb = new StringBuilder();
+ if (subscriptionInfo != null) {
+ final TelephonyManager telephonyManager =
+ (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
+ final int count = subscriptionInfo.size();
+ for (int i = 0; i < count; i++) {
+ final String rawNumber = telephonyManager.getLine1Number(
+ subscriptionInfo.get(i).getSubscriptionId());
+ if (!TextUtils.isEmpty(rawNumber)) {
+ sb.append(PhoneNumberUtils.formatNumber(rawNumber));
+ if (i < count - 1) {
+ sb.append("\n");
+ }
+ }
+ }
+ }
+ return sb.toString();
+ }
+
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java b/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
index 83a2123..b5295da 100644
--- a/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
@@ -23,7 +23,9 @@
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
import android.net.Uri;
import android.provider.Settings.Global;
import android.text.TextUtils;
@@ -139,7 +141,7 @@
try {
Intent intent = Intent.parseUri(helpUriString,
Intent.URI_ANDROID_APP_SCHEME | Intent.URI_INTENT_SCHEME);
- addIntentParameters(context, intent, backupContext);
+ addIntentParameters(context, intent, backupContext, true /* sendPackageName */);
ComponentName component = intent.resolveActivity(context.getPackageManager());
if (component != null) {
return intent;
@@ -163,16 +165,32 @@
return intent;
}
- private static void addIntentParameters(Context context, Intent intent, String backupContext) {
+ public static void addIntentParameters(Context context, Intent intent, String backupContext,
+ boolean sendPackageName) {
if (!intent.hasExtra(EXTRA_CONTEXT)) {
// Insert some context if none exists.
intent.putExtra(EXTRA_CONTEXT, backupContext);
}
+
+ Resources resources = context.getResources();
+ boolean includePackageName = resources.getBoolean(R.bool.config_sendPackageName);
+
+ if (sendPackageName && includePackageName) {
+ String[] packageNameKey =
+ {resources.getString(R.string.config_helpPackageNameKey)};
+ String[] packageNameValue =
+ {resources.getString(R.string.config_helpPackageNameValue)};
+ String intentExtraKey =
+ resources.getString(R.string.config_helpIntentExtraKey);
+ String intentNameKey =
+ resources.getString(R.string.config_helpIntentNameKey);
+ intent.putExtra(intentExtraKey, packageNameKey);
+ intent.putExtra(intentNameKey, packageNameValue);
+ }
intent.putExtra(EXTRA_THEME, 1 /* Light, dark action bar */);
- Theme theme = context.getTheme();
- TypedValue typedValue = new TypedValue();
- theme.resolveAttribute(android.R.attr.colorPrimary, typedValue, true);
- intent.putExtra(EXTRA_PRIMARY_COLOR, context.getColor(typedValue.resourceId));
+ TypedArray array = context.obtainStyledAttributes(new int[]{android.R.attr.colorPrimary});
+ intent.putExtra(EXTRA_PRIMARY_COLOR, array.getColor(0, 0));
+ array.recycle();
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
index d368de9..151e0ea 100644
--- a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
@@ -16,18 +16,11 @@
package com.android.settingslib;
import android.content.Context;
-import android.net.wifi.WifiManager;
import android.os.SystemProperties;
import android.telephony.CarrierConfigManager;
public class TetherUtil {
- public static boolean setWifiTethering(boolean enable, Context context) {
- final WifiManager wifiManager =
- (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- return wifiManager.setWifiApEnabled(null, enable);
- }
-
private static boolean isEntitlementCheckRequired(Context context) {
final CarrierConfigManager configManager = (CarrierConfigManager) context
.getSystemService(Context.CARRIER_CONFIG_SERVICE);
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index d98f1a4..e049079 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -1,5 +1,6 @@
package com.android.settingslib;
+import android.annotation.ColorInt;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
@@ -8,12 +9,14 @@
import android.content.pm.UserInfo;
import android.content.pm.Signature;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.os.BatteryManager;
import android.os.UserManager;
+import android.print.PrintManager;
import com.android.internal.util.UserIcons;
import com.android.settingslib.drawable.UserIconDrawable;
@@ -154,11 +157,19 @@
return statusString;
}
+ @ColorInt
+ public static int getColorAccent(Context context) {
+ TypedArray ta = context.obtainStyledAttributes(new int[]{android.R.attr.colorAccent});
+ @ColorInt int colorAccent = ta.getColor(0, 0);
+ ta.recycle();
+ return colorAccent;
+ }
+
/**
* Determine whether a package is a "system package", in which case certain things (like
* disabling notifications or disabling the package altogether) should be disallowed.
*/
- public static boolean isSystemPackage(PackageManager pm, PackageInfo pkg) {
+ public static boolean isSystemPackage(Resources resources, PackageManager pm, PackageInfo pkg) {
if (sSystemSignature == null) {
sSystemSignature = new Signature[]{ getSystemSignature(pm) };
}
@@ -175,7 +186,9 @@
&& sSystemSignature[0].equals(getFirstSignature(pkg)))
|| pkg.packageName.equals(sPermissionControllerPackageName)
|| pkg.packageName.equals(sServicesSystemSharedLibPackageName)
- || pkg.packageName.equals(sSharedSystemSharedLibPackageName);
+ || pkg.packageName.equals(sSharedSystemSharedLibPackageName)
+ || pkg.packageName.equals(PrintManager.PRINT_SPOOLER_PACKAGE_NAME)
+ || isDeviceProvisioningPackage(resources, pkg.packageName);
}
private static Signature getFirstSignature(PackageInfo pkg) {
@@ -193,4 +206,14 @@
}
return null;
}
+
+ /**
+ * Returns {@code true} if the supplied package is the device provisioning app. Otherwise,
+ * returns {@code false}.
+ */
+ public static boolean isDeviceProvisioningPackage(Resources resources, String packageName) {
+ String deviceProvisioningPackage = resources.getString(
+ com.android.internal.R.string.config_deviceProvisioningPackage);
+ return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 7392453..a22a051 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -582,10 +582,10 @@
public ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator,
boolean foreground) {
synchronized (mRebuildSync) {
- synchronized (mEntriesMap) {
+ synchronized (mRebuildingSessions) {
mRebuildingSessions.add(this);
mRebuildRequested = true;
- mRebuildAsync = false;
+ mRebuildAsync = true;
mRebuildFilter = filter;
mRebuildComparator = comparator;
mRebuildForeground = foreground;
@@ -597,23 +597,7 @@
}
}
- // We will wait for .25s for the list to be built.
- long waitend = SystemClock.uptimeMillis()+250;
-
- while (mRebuildResult == null) {
- long now = SystemClock.uptimeMillis();
- if (now >= waitend) {
- break;
- }
- try {
- mRebuildSync.wait(waitend - now);
- } catch (InterruptedException e) {
- }
- }
-
- mRebuildAsync = true;
-
- return mRebuildResult;
+ return null;
}
}
@@ -765,6 +749,7 @@
static final int MSG_LOAD_ICONS = 3;
static final int MSG_LOAD_SIZES = 4;
static final int MSG_LOAD_LAUNCHER = 5;
+ static final int MSG_LOAD_HOME_APP = 6;
boolean mRunning;
@@ -776,7 +761,7 @@
public void handleMessage(Message msg) {
// Always try rebuilding list first thing, if needed.
ArrayList<Session> rebuildingSessions = null;
- synchronized (mEntriesMap) {
+ synchronized (mRebuildingSessions) {
if (mRebuildingSessions.size() > 0) {
rebuildingSessions = new ArrayList<Session>(mRebuildingSessions);
mRebuildingSessions.clear();
@@ -833,13 +818,33 @@
if (!mMainHandler.hasMessages(MainHandler.MSG_LOAD_ENTRIES_COMPLETE)) {
mMainHandler.sendEmptyMessage(MainHandler.MSG_LOAD_ENTRIES_COMPLETE);
}
- sendEmptyMessage(MSG_LOAD_LAUNCHER);
+ sendEmptyMessage(MSG_LOAD_HOME_APP);
}
} break;
+ case MSG_LOAD_HOME_APP: {
+ final List<ResolveInfo> homeActivities = new ArrayList<>();
+ mPm.getHomeActivities(homeActivities);
+ synchronized (mEntriesMap) {
+ final int entryCount = mEntriesMap.size();
+ for (int i = 0; i < entryCount; i++) {
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP acquired lock");
+ final HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
+ for (ResolveInfo activity : homeActivities) {
+ String packageName = activity.activityInfo.packageName;
+ AppEntry entry = userEntries.get(packageName);
+ if (entry != null) {
+ entry.isHomeApp = true;
+ }
+ }
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP releasing lock");
+ }
+ }
+ sendEmptyMessage(MSG_LOAD_LAUNCHER);
+ }
+ break;
case MSG_LOAD_LAUNCHER: {
Intent launchIntent = new Intent(Intent.ACTION_MAIN, null)
.addCategory(Intent.CATEGORY_LAUNCHER);
-
for (int i = 0; i < mEntriesMap.size(); i++) {
int userId = mEntriesMap.keyAt(i);
// If we do not specify MATCH_DIRECT_BOOT_AWARE or
@@ -1135,6 +1140,11 @@
*/
public boolean hasLauncherEntry;
+ /**
+ * Whether or not it's a Home app.
+ */
+ public boolean isHomeApp;
+
public String getNormalizedLabel() {
if (normalizedLabel != null) {
return normalizedLabel;
@@ -1326,6 +1336,8 @@
return true;
} else if (entry.hasLauncherEntry) {
return true;
+ } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0 && entry.isHomeApp) {
+ return true;
}
return false;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index c075703..a879d16f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -809,7 +809,9 @@
// No separate prompt is displayed after pairing.
if (getPhonebookPermissionChoice() == CachedBluetoothDevice.ACCESS_UNKNOWN) {
if (mDevice.getBluetoothClass().getDeviceClass()
- == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE) {
+ == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE ||
+ mDevice.getBluetoothClass().getDeviceClass()
+ == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET) {
setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_ALLOWED);
} else {
setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_REJECTED);
diff --git a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
index 78d7c56..a99e668 100644
--- a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.res.Resources;
+import android.hardware.display.DisplayManager;
import android.os.AsyncTask;
import android.os.RemoteException;
import android.util.DisplayMetrics;
@@ -95,7 +96,9 @@
}
final Resources res = context.getResources();
- final DisplayMetrics metrics = res.getDisplayMetrics();
+ final DisplayMetrics metrics = new DisplayMetrics();
+ context.getDisplay().getRealMetrics(metrics);
+
final int currentDensity = metrics.densityDpi;
int currentDensityIndex = -1;
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 37e3c53..6658c14 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -41,8 +41,10 @@
import android.view.Window;
import android.view.WindowManager.LayoutParams;
import android.widget.AdapterView;
+import android.widget.FrameLayout;
import android.widget.ListView;
import android.widget.Toolbar;
+
import com.android.settingslib.R;
import com.android.settingslib.applications.InterestingConfigChanges;
@@ -68,6 +70,7 @@
private final List<CategoryListener> mCategoryListeners = new ArrayList<>();
private SettingsDrawerAdapter mDrawerAdapter;
+ private FrameLayout mContentHeaderContainer;
private DrawerLayout mDrawerLayout;
private boolean mShowingMenu;
@@ -84,6 +87,7 @@
requestWindowFeature(Window.FEATURE_NO_TITLE);
}
super.setContentView(R.layout.settings_with_drawer);
+ mContentHeaderContainer = (FrameLayout) findViewById(R.id.content_header_container);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
if (mDrawerLayout == null) {
return;
@@ -180,6 +184,13 @@
}
}
+ public void setContentHeaderView(View headerView) {
+ mContentHeaderContainer.removeAllViews();
+ if (headerView != null) {
+ mContentHeaderContainer.addView(headerView);
+ }
+ }
+
@Override
public void setContentView(@LayoutRes int layoutResID) {
final ViewGroup parent = (ViewGroup) findViewById(R.id.content_frame);
@@ -272,6 +283,13 @@
}
}
+ public HashMap<Pair<String, String>, Tile> getTileCache() {
+ if (sTileCache == null) {
+ getDashboardCategories();
+ }
+ return sTileCache;
+ }
+
public void onProfileTileOpen() {
finish();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 1664c89..e70cc29 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -307,7 +307,7 @@
return false;
}
- private static final Comparator<Tile> TILE_COMPARATOR =
+ public static final Comparator<Tile> TILE_COMPARATOR =
new Comparator<Tile>() {
@Override
public int compare(Tile lhs, Tile rhs) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/UsageView.java b/packages/SettingsLib/src/com/android/settingslib/graph/UsageView.java
index ee1821d..c6a45bc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/UsageView.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/UsageView.java
@@ -71,10 +71,11 @@
layout.addView(labels);
// Set gravity.
labels.setGravity(Gravity.END);
- // Swap the bottom label padding
+ // Swap the bottom space order.
LinearLayout bottomLabels = (LinearLayout) findViewById(R.id.bottom_label_group);
- bottomLabels.setPadding(bottomLabels.getPaddingRight(), bottomLabels.getPaddingTop(),
- bottomLabels.getPaddingLeft(), bottomLabels.getPaddingBottom());
+ View bottomSpace = bottomLabels.findViewById(R.id.bottom_label_space);
+ bottomLabels.removeView(bottomSpace);
+ bottomLabels.addView(bottomSpace);
} else if (gravity != Gravity.START) {
throw new IllegalArgumentException("Unsupported gravity " + gravity);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index e53dd2f..994ea88 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -32,6 +32,8 @@
import android.text.format.Time;
import android.util.Log;
+import com.android.settingslib.R;
+
import java.util.Date;
import java.util.Locale;
@@ -41,12 +43,12 @@
import static android.telephony.TelephonyManager.SIM_STATE_READY;
import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
+import static android.net.TrafficStats.MB_IN_BYTES;
public class DataUsageController {
+
private static final String TAG = "DataUsageController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
- public static final long DEFAULT_WARNING_LEVEL = 2L * 1024 * 1024 * 1024;
private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES;
private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50);
private static final java.util.Formatter PERIOD_FORMATTER = new java.util.Formatter(
@@ -75,6 +77,14 @@
mNetworkController = networkController;
}
+ /**
+ * Returns the default warning level in bytes.
+ */
+ public long getDefaultWarningLevel() {
+ return MB_IN_BYTES
+ * mContext.getResources().getInteger(R.integer.default_data_warning_level_mb);
+ }
+
private INetworkStatsSession getSession() {
if (mSession == null) {
try {
@@ -169,7 +179,7 @@
usage.limitLevel = policy.limitBytes > 0 ? policy.limitBytes : 0;
usage.warningLevel = policy.warningBytes > 0 ? policy.warningBytes : 0;
} else {
- usage.warningLevel = DEFAULT_WARNING_LEVEL;
+ usage.warningLevel = getDefaultWarningLevel();
}
if (usage != null && mNetworkController != null) {
usage.carrier = mNetworkController.getMobileDataNetworkName();
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 978ca94..f7e9541 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -153,7 +153,7 @@
<integer name="def_download_manager_recommended_max_bytes_over_mobile">-1</integer>
<!-- Default for Settings.Secure.LONG_PRESS_TIMEOUT_MILLIS -->
- <integer name="def_long_press_timeout_millis">500</integer>
+ <integer name="def_long_press_timeout_millis">400</integer>
<!-- Default for Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD -->
<bool name="def_show_ime_with_hard_keyboard">false</bool>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/EventLogTags.logtags b/packages/SettingsProvider/src/com/android/providers/settings/EventLogTags.logtags
index 298d776..7eff16b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/EventLogTags.logtags
+++ b/packages/SettingsProvider/src/com/android/providers/settings/EventLogTags.logtags
@@ -1,5 +1,6 @@
-# See system/core/logcat/e for a description of the format of this file.
+# See system/core/logcat/event.logtags for a description of the format of this file.
option java_package com.android.providers.settings;
52100 unsupported_settings_query (uri|3),(selection|3),(whereArgs|3)
+52101 persist_setting_error (message|3)
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index b79ce80..bf48e5d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -247,7 +247,6 @@
return Settings.Secure.getInt(mContext.getContentResolver(), name, 0) != 0;
case Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES:
case Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES:
- case Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX:
case Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER:
case Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE:
return !TextUtils.isEmpty(Settings.Secure.getString(
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 90eade4..0f7fe6f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -544,7 +544,7 @@
final int userCount = users.size();
for (int i = 0; i < userCount; i++) {
UserInfo user = users.get(i);
- dumpForUser(user.id, pw);
+ dumpForUserLocked(user.id, pw);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -552,12 +552,16 @@
}
}
- private void dumpForUser(int userId, PrintWriter pw) {
+ private void dumpForUserLocked(int userId, PrintWriter pw) {
if (userId == UserHandle.USER_SYSTEM) {
pw.println("GLOBAL SETTINGS (user " + userId + ")");
Cursor globalCursor = getAllGlobalSettings(ALL_COLUMNS);
dumpSettings(globalCursor, pw);
pw.println();
+
+ SettingsState globalSettings = mSettingsRegistry.getSettingsLocked(
+ SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
+ globalSettings.dumpHistoricalOperations(pw);
}
pw.println("SECURE SETTINGS (user " + userId + ")");
@@ -565,10 +569,18 @@
dumpSettings(secureCursor, pw);
pw.println();
+ SettingsState secureSettings = mSettingsRegistry.getSettingsLocked(
+ SETTINGS_TYPE_SECURE, userId);
+ secureSettings.dumpHistoricalOperations(pw);
+
pw.println("SYSTEM SETTINGS (user " + userId + ")");
Cursor systemCursor = getAllSystemSettings(userId, ALL_COLUMNS);
dumpSettings(systemCursor, pw);
pw.println();
+
+ SettingsState systemSettings = mSettingsRegistry.getSettingsLocked(
+ SETTINGS_TYPE_SYSTEM, userId);
+ systemSettings.dumpHistoricalOperations(pw);
}
private void dumpSettings(Cursor cursor, PrintWriter pw) {
@@ -2090,7 +2102,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 127;
+ private static final int SETTINGS_VERSION = 130;
private final int mUserId;
@@ -2345,6 +2357,68 @@
currentVersion = 127;
}
+ if (currentVersion == 127) {
+ // version 127 is no longer used.
+ currentVersion = 128;
+ }
+
+ if (currentVersion == 128) {
+ // Version 128: Allow OEMs to grant DND access to default apps. Note that
+ // the new apps are appended to the list of already approved apps.
+ final SettingsState systemSecureSettings =
+ getSecureSettingsLocked(userId);
+
+ final Setting policyAccess = systemSecureSettings.getSettingLocked(
+ Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES);
+ String defaultPolicyAccess = getContext().getResources().getString(
+ com.android.internal.R.string.config_defaultDndAccessPackages);
+ if (!TextUtils.isEmpty(defaultPolicyAccess)) {
+ if (policyAccess.isNull()) {
+ systemSecureSettings.insertSettingLocked(
+ Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
+ defaultPolicyAccess,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ } else {
+ StringBuilder currentSetting =
+ new StringBuilder(policyAccess.getValue());
+ currentSetting.append(":");
+ currentSetting.append(defaultPolicyAccess);
+ systemSecureSettings.updateSettingLocked(
+ Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
+ currentSetting.toString(),
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ }
+
+ currentVersion = 129;
+ }
+
+ if (currentVersion == 129) {
+ // default longpress timeout changed from 500 to 400. If unchanged from the old
+ // default, update to the new default.
+ final SettingsState systemSecureSettings =
+ getSecureSettingsLocked(userId);
+ final String oldValue = systemSecureSettings.getSettingLocked(
+ Settings.Secure.LONG_PRESS_TIMEOUT).getValue();
+ if (TextUtils.equals("500", oldValue)) {
+ systemSecureSettings.insertSettingLocked(
+ Settings.Secure.LONG_PRESS_TIMEOUT,
+ String.valueOf(getContext().getResources().getInteger(
+ R.integer.def_long_press_timeout_millis)),
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ currentVersion = 130;
+ }
+
+ if (currentVersion != newVersion) {
+ Slog.w("SettingsProvider", "warning: upgrading settings database to version "
+ + newVersion + " left it at "
+ + currentVersion + " instead; this is probably a bug", new Throwable());
+ if (DEBUG) {
+ throw new RuntimeException("db upgrade error");
+ }
+ }
+
// vXXX: Add new settings above this point.
// Return the current version.
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 4710d5a..bc91545 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -16,6 +16,7 @@
package com.android.providers.settings;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -26,6 +27,7 @@
import android.util.AtomicFile;
import android.util.Base64;
import android.util.Slog;
+import android.util.TimeUtils;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import libcore.io.IoUtils;
@@ -39,6 +41,7 @@
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@@ -60,7 +63,7 @@
private static final String LOG_TAG = "SettingsState";
- static final int SETTINGS_VERSOIN_NEW_ENCODING = 121;
+ static final int SETTINGS_VERSION_NEW_ENCODING = 121;
private static final long WRITE_SETTINGS_DELAY_MILLIS = 200;
private static final long MAX_WRITE_SETTINGS_DELAY_MILLIS = 2000;
@@ -93,6 +96,12 @@
// This was used in version 120 and before.
private static final String NULL_VALUE_OLD_STYLE = "null";
+ private static final int HISTORICAL_OPERATION_COUNT = 20;
+ private static final String HISTORICAL_OPERATION_UPDATE = "update";
+ private static final String HISTORICAL_OPERATION_DELETE = "delete";
+ private static final String HISTORICAL_OPERATION_PERSIST = "persist";
+ private static final String HISTORICAL_OPERATION_INITIALIZE = "initialize";
+
private final Object mLock;
private final Handler mHandler;
@@ -117,6 +126,9 @@
};
@GuardedBy("mLock")
+ private final List<HistoricalOperation> mHistoricalOperations;
+
+ @GuardedBy("mLock")
public final int mKey;
@GuardedBy("mLock")
@@ -134,6 +146,9 @@
@GuardedBy("mLock")
private long mNextId;
+ @GuardedBy("mLock")
+ private int mNextHistoricalOpIdx;
+
public SettingsState(Object lock, File file, int key, int maxBytesPerAppPackage,
Looper looper) {
// It is important that we use the same lock as the settings provider
@@ -150,6 +165,10 @@
mMaxBytesPerAppPackage = maxBytesPerAppPackage;
mPackageToMemoryUsage = null;
}
+
+ mHistoricalOperations = Build.IS_DEBUGGABLE
+ ? new ArrayList<>(HISTORICAL_OPERATION_COUNT) : null;
+
synchronized (mLock) {
readStateSyncLocked();
}
@@ -238,16 +257,20 @@
Setting oldState = mSettings.get(name);
String oldValue = (oldState != null) ? oldState.value : null;
+ Setting newState;
if (oldState != null) {
if (!oldState.update(value, packageName)) {
return false;
}
+ newState = oldState;
} else {
- Setting state = new Setting(name, value, packageName);
- mSettings.put(name, state);
+ newState = new Setting(name, value, packageName);
+ mSettings.put(name, newState);
}
+ addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState);
+
updateMemoryUsagePerPackageLocked(packageName, oldValue, value);
scheduleWriteIfNeededLocked();
@@ -271,6 +294,8 @@
updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value, null);
+ addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState);
+
scheduleWriteIfNeededLocked();
return true;
@@ -290,6 +315,51 @@
}
}
+ private void addHistoricalOperationLocked(String type, Setting setting) {
+ if (mHistoricalOperations == null) {
+ return;
+ }
+ HistoricalOperation operation = new HistoricalOperation(
+ SystemClock.elapsedRealtime(), type,
+ setting != null ? new Setting(setting) : null);
+ if (mNextHistoricalOpIdx >= mHistoricalOperations.size()) {
+ mHistoricalOperations.add(operation);
+ } else {
+ mHistoricalOperations.set(mNextHistoricalOpIdx, operation);
+ }
+ mNextHistoricalOpIdx++;
+ if (mNextHistoricalOpIdx >= HISTORICAL_OPERATION_COUNT) {
+ mNextHistoricalOpIdx = 0;
+ }
+ }
+
+ public void dumpHistoricalOperations(PrintWriter pw) {
+ synchronized (mLock) {
+ if (mHistoricalOperations == null) {
+ return;
+ }
+ pw.println("Historical operations");
+ final int operationCount = mHistoricalOperations.size();
+ for (int i = 0; i < operationCount; i++) {
+ int index = mNextHistoricalOpIdx - 1 - i;
+ if (index < 0) {
+ index = operationCount + index;
+ }
+ HistoricalOperation operation = mHistoricalOperations.get(index);
+ pw.print(TimeUtils.formatForLogging(operation.mTimestamp));
+ pw.print(" ");
+ pw.print(operation.mOperation);
+ if (operation.mSetting != null) {
+ pw.print(" ");
+ pw.print(operation.mSetting);
+ }
+ pw.println();
+ }
+ pw.println();
+ pw.println();
+ }
+ }
+
private void updateMemoryUsagePerPackageLocked(String packageName, String oldValue,
String newValue) {
if (mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED) {
@@ -407,6 +477,10 @@
serializer.endDocument();
destination.finishWrite(out);
+ synchronized (mLock) {
+ addHistoricalOperationLocked(HISTORICAL_OPERATION_PERSIST, null);
+ }
+
if (DEBUG_PERSISTENCE) {
Slog.i(LOG_TAG, "[PERSIST END]");
}
@@ -435,7 +509,7 @@
static void setValueAttribute(int version, XmlSerializer serializer, String value)
throws IOException {
- if (version >= SETTINGS_VERSOIN_NEW_ENCODING) {
+ if (version >= SETTINGS_VERSION_NEW_ENCODING) {
if (value == null) {
// Null value -> No ATTR_VALUE nor ATTR_VALUE_BASE64.
} else if (isBinary(value)) {
@@ -454,7 +528,7 @@
}
private String getValueAttribute(XmlPullParser parser) {
- if (mVersion >= SETTINGS_VERSOIN_NEW_ENCODING) {
+ if (mVersion >= SETTINGS_VERSION_NEW_ENCODING) {
final String value = parser.getAttributeValue(null, ATTR_VALUE);
if (value != null) {
return value;
@@ -479,22 +553,26 @@
private void readStateSyncLocked() {
FileInputStream in;
if (!mStatePersistFile.exists()) {
+ Slog.i(LOG_TAG, "No settings state " + mStatePersistFile);
+ addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null);
return;
}
try {
in = new AtomicFile(mStatePersistFile).openRead();
} catch (FileNotFoundException fnfe) {
- Slog.i(LOG_TAG, "No settings state");
+ String message = "No settings state " + mStatePersistFile;
+ Slog.wtf(LOG_TAG, message);
+ Slog.i(LOG_TAG, message);
return;
}
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(in, StandardCharsets.UTF_8.name());
parseStateLocked(parser);
-
} catch (XmlPullParserException | IOException e) {
- throw new IllegalStateException("Failed parsing settings file: "
- + mStatePersistFile , e);
+ String message = "Failed parsing settings file: " + mStatePersistFile;
+ Slog.wtf(LOG_TAG, message);
+ throw new IllegalStateException(message , e);
} finally {
IoUtils.closeQuietly(in);
}
@@ -567,6 +645,19 @@
}
}
+ private class HistoricalOperation {
+ final long mTimestamp;
+ final String mOperation;
+ final Setting mSetting;
+
+ public HistoricalOperation(long timestamp,
+ String operation, Setting setting) {
+ mTimestamp = timestamp;
+ mOperation = operation;
+ mSetting = setting;
+ }
+ }
+
class Setting {
private String name;
private String value;
@@ -629,6 +720,10 @@
this.id = String.valueOf(mNextId++);
return true;
}
+
+ public String toString() {
+ return "Setting{name=" + value + " from " + packageName + "}";
+ }
}
/**
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index b5bd8ad..9964467 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -15,7 +15,6 @@
*/
package com.android.providers.settings;
-import android.os.Handler;
import android.os.Looper;
import android.test.AndroidTestCase;
import android.util.Xml;
@@ -99,10 +98,10 @@
checkWriteSingleSetting(serializer, null, null);
checkWriteSingleSetting(serializer, CRAZY_STRING, null);
SettingsState.writeSingleSetting(
- SettingsState.SETTINGS_VERSOIN_NEW_ENCODING,
+ SettingsState.SETTINGS_VERSION_NEW_ENCODING,
serializer, null, "k", "v", "package");
SettingsState.writeSingleSetting(
- SettingsState.SETTINGS_VERSOIN_NEW_ENCODING,
+ SettingsState.SETTINGS_VERSION_NEW_ENCODING,
serializer, "1", "k", "v", null);
}
@@ -115,7 +114,7 @@
String key, String value) throws Exception {
// Make sure the XML serializer won't crash.
SettingsState.writeSingleSetting(
- SettingsState.SETTINGS_VERSOIN_NEW_ENCODING,
+ SettingsState.SETTINGS_VERSION_NEW_ENCODING,
serializer, "1", key, value, "package");
}
@@ -129,7 +128,7 @@
final SettingsState ssWriter = new SettingsState(lock, file, 1,
SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
- ssWriter.setVersionLocked(SettingsState.SETTINGS_VERSOIN_NEW_ENCODING);
+ ssWriter.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING);
ssWriter.insertSettingLocked("k1", "\u0000", "package");
ssWriter.insertSettingLocked("k2", "abc", "p2");
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 2efefb3..f1789ea 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -142,7 +142,7 @@
<activity
android:name=".BugreportWarningActivity"
- android:theme="@android:style/Theme.Material.Light.Dialog.Alert"
+ android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true"
android:exported="false" />
diff --git a/packages/Shell/res/layout/confirm_repeat.xml b/packages/Shell/res/layout/confirm_repeat.xml
index ad90af1..9f1d53e 100644
--- a/packages/Shell/res/layout/confirm_repeat.xml
+++ b/packages/Shell/res/layout/confirm_repeat.xml
@@ -18,10 +18,10 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingStart="16dip"
- android:paddingEnd="16dip"
- android:paddingTop="8dip"
- android:paddingBottom="16dip"
+ android:paddingStart="24dip"
+ android:paddingEnd="24dip"
+ android:paddingTop="24dip"
+ android:paddingBottom="8dip"
android:orientation="vertical"
android:keepScreenOn="true">
<ScrollView
diff --git a/packages/Shell/res/values-af/strings.xml b/packages/Shell/res/values-af/strings.xml
index 51679f7..391017e 100644
--- a/packages/Shell/res/values-af/strings.xml
+++ b/packages/Shell/res/values-af/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Foutverslag <xliff:g id="ID">#%d</xliff:g> is vasgevang"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Voeg tans besonderhede by die foutverslag"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Wag asseblief …"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Swiep na links om jou foutverslag te deel"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Die foutverslag sal binnekort op die foon verskyn"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Tik om jou foutverslag te deel"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Tik om jou foutverslag sonder \'n skermkiekie te deel, of wag totdat die skermkiekie gereed is"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Tik om jou foutverslag sonder \'n skermkiekie te deel, of wag totdat die skermkiekie gereed is"</string>
diff --git a/packages/Shell/res/values-am/strings.xml b/packages/Shell/res/values-am/strings.xml
index f04e882..dccf9dd 100644
--- a/packages/Shell/res/values-am/strings.xml
+++ b/packages/Shell/res/values-am/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"የሳንካ ሪፖርት <xliff:g id="ID">#%d</xliff:g> ተወስዷል"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"ዝርዝሮችን ወደ የሳንካ ሪፖርቱ በማከል ላይ"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"እባክዎ ይጠብቁ…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"የሳንካ ሪፖርትዎን ለማጋራት ወደ ግራ ያንሸራትቱ"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"የሳንካ ሪፖርቱ ከትንሽ ጊዜ በኋላ በስልኩ ላይ ይመጣል"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"የሳንካ ሪፖርትዎን ለማጋራት መታ ያድርጉ"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"የእርስዎን የሳንካ ሪፖርት ያለ ቅጽበታዊ ማያ ገጽ ለማጋራት መታ ያድርጉ ወይም ቅጽበታዊ ማያ ገጹ እስኪጨርስ ይጠብቁ"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"የእርስዎን የሳንካ ሪፖርት ያለ ቅጽበታዊ ማያ ገጽ ለማጋራት መታ ያድርጉ ወይም ቅጽበታዊ ማያ ገጹ እስኪጨርስ ይጠብቁ"</string>
diff --git a/packages/Shell/res/values-ar/strings.xml b/packages/Shell/res/values-ar/strings.xml
index 37516a1..d2e3cc5 100644
--- a/packages/Shell/res/values-ar/strings.xml
+++ b/packages/Shell/res/values-ar/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"تم تسجيل تقرير الخطأ <xliff:g id="ID">#%d</xliff:g>."</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"إضافة تفاصيل إلى تقرير الخطأ"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"الرجاء الانتظار…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"مرر بسرعة لليمين لمشاركة تقرير الخطأ"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"سيظهر تقرير الخطأ على الهاتف بعد قليل"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"انقر لمشاركة تقرير الخطأ."</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"انقر لمشاركة تقرير الأخطاء بدون لقطة شاشة أو انتظر حتى انتهاء لقطة الشاشة"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"انقر لمشاركة تقرير الأخطاء بدون لقطة شاشة أو انتظر حتى انتهاء لقطة الشاشة"</string>
diff --git a/packages/Shell/res/values-az-rAZ/strings.xml b/packages/Shell/res/values-az-rAZ/strings.xml
index 303467b..9e6c84b 100644
--- a/packages/Shell/res/values-az-rAZ/strings.xml
+++ b/packages/Shell/res/values-az-rAZ/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Baq hesabatı <xliff:g id="ID">#%d</xliff:g> alındı"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Detallar baq hesabatına əlavə olunur"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Lütfən, gözləyin..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Baq raportunu paylaşmaq üçün sola sürüşdürün"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Baq həlli tezliklə telefonda görünəcək"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Baq hesabatınızı paylaşmaq üçün tıklayın"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"baq hesabatınızı skrinşot olmadan paylaşmaq üçün tıklayın, skrinşotun tamamlanması üçün isə gözləyin"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"baq hesabatınızı skrinşot olmadan paylaşmaq üçün tıklayın, skrinşotun tamamlanması üçün isə gözləyin"</string>
diff --git a/packages/Shell/res/values-b+sr+Latn/strings.xml b/packages/Shell/res/values-b+sr+Latn/strings.xml
index 185b690..3c7c7e8 100644
--- a/packages/Shell/res/values-b+sr+Latn/strings.xml
+++ b/packages/Shell/res/values-b+sr+Latn/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Izveštaj o grešci <xliff:g id="ID">#%d</xliff:g> je snimljen"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Dodaju se detalji u izveštaj o grešci"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Sačekajte..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Prevucite ulevo da biste delili izveštaj o greškama"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Izveštaj o grešci će se uskoro pojaviti na telefonu"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Dodirnite da biste delili izveštaj o grešci"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Dodirnite za deljenje izveštaja o grešci bez snimka ekrana ili sačekajte da se napravi snimak ekrana"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Dodirnite za deljenje izveštaja o grešci bez snimka ekrana ili sačekajte da se napravi snimak ekrana"</string>
diff --git a/packages/Shell/res/values-be-rBY/strings.xml b/packages/Shell/res/values-be-rBY/strings.xml
index fb29fbc..7e7fc79 100644
--- a/packages/Shell/res/values-be-rBY/strings.xml
+++ b/packages/Shell/res/values-be-rBY/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Справаздача <xliff:g id="ID">#%d</xliff:g> пра памылку зафіксавана"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Дадаванне падрабязнасцей да справаздачы пра памылкі"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Калі ласка, пачакайце..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Правядзіце пальцам налева, каб абагуліць сваю справаздачу пра памылку"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Паведамленне пра памылку хутка з\'явіцца на тэлефоне"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Дакраніцеся, каб абагуліць сваю справаздачу пра памылку"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Краніце, каб абагуліць справаздачу пра памылку без здымка экрана, або чакайце атрымання здымка."</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Краніце, каб абагуліць справаздачу пра памылку без здымка экрана, або чакайце атрымання здымка."</string>
diff --git a/packages/Shell/res/values-bg/strings.xml b/packages/Shell/res/values-bg/strings.xml
index 0787900..ed8caaf 100644
--- a/packages/Shell/res/values-bg/strings.xml
+++ b/packages/Shell/res/values-bg/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Сигналът за програмна грешка „<xliff:g id="ID">#%d</xliff:g>“ е заснет"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Подробностите се добавят към сигнала за пр. грешка"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Моля, изчакайте…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Прекарайте пръст наляво, за да споделите сигнала си за програмна грешка"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Сигналът за програмна грешка скоро ще се покаже на телефона"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Докоснете, за да споделите сигнала си за програмна грешка"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Докоснете, за да споделите сигнала за прогр. грешка без екранна снимка, или изчакайте завършването й"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Докоснете, за да споделите сигнала за прогр. грешка без екранна снимка, или изчакайте завършването й"</string>
diff --git a/packages/Shell/res/values-bn-rBD/strings.xml b/packages/Shell/res/values-bn-rBD/strings.xml
index a51950a..b7d550c 100644
--- a/packages/Shell/res/values-bn-rBD/strings.xml
+++ b/packages/Shell/res/values-bn-rBD/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"ত্রুটির প্রতিবেদন <xliff:g id="ID">#%d</xliff:g> ক্যাপচার করা হয়েছে"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"ত্রুটির প্রতিবেদনে বিশদ বিবরণ যোগ করা হচ্ছে"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"অনুগ্রহ করে অপেক্ষা করুন..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"আপনার বাগ রিপোর্ট শেয়ার করতে বামে সোয়াইপ করুন"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"ফোনে শীঘ্রই ত্রুটির প্রতিবেদন দেখা যাবে"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"আপনার ত্রুটির প্রতিবেদন শেয়ার করতে আলতো চাপ দিন"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"কোনো স্ক্রীনশট ছাড়াই ত্রুটির প্রতিবেদন শেয়ার করতে আলতো চাপ দিন বা সম্পন্ন করতে স্ক্রীনশটের জন্য অপেক্ষা করুন"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"কোনো স্ক্রীনশট ছাড়াই ত্রুটির প্রতিবেদন শেয়ার করতে আলতো চাপ দিন বা সম্পন্ন করতে স্ক্রীনশটের জন্য অপেক্ষা করুন"</string>
diff --git a/packages/Shell/res/values-bs-rBA/strings.xml b/packages/Shell/res/values-bs-rBA/strings.xml
index 80dddb5..88d247d 100644
--- a/packages/Shell/res/values-bs-rBA/strings.xml
+++ b/packages/Shell/res/values-bs-rBA/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Izvještaj o grešci <xliff:g id="ID">#%d</xliff:g> je snimljen"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Dodavanje detalja u izvještaj o greškama"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Pričekajte..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Prevucite lijevo da podijelite izvještaj o greškama"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Izvještaj o greškama će se ubrzo pojaviti na ekranu"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Dodirnite da biste podijelili izvještaj o grešci"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Dodirnite da podijelite izveštaj o greškama bez snimka ekrana ili sačekajte da snimak bude gotov"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Dodirnite da podijelite izveštaj o greškama bez snimka ekrana ili sačekajte da snimak bude gotov"</string>
diff --git a/packages/Shell/res/values-ca/strings.xml b/packages/Shell/res/values-ca/strings.xml
index f8ed813..d570a54 100644
--- a/packages/Shell/res/values-ca/strings.xml
+++ b/packages/Shell/res/values-ca/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"S\'ha capturat l\'informe d\'errors <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"S\'estan afegint detalls a l\'informe d\'errors"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Espera…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Llisca cap a l\'esquerra per compartir l\'informe d\'errors."</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"L\'informe d\'errors es mostrarà al telèfon aviat"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Toca per compartir l\'informe d\'errors"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Toca per compartir l\'informe d\'errors sense captura de pantalla o espera que es creï la captura"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Toca per compartir l\'informe d\'errors sense captura de pantalla o espera que es creï la captura"</string>
diff --git a/packages/Shell/res/values-cs/strings.xml b/packages/Shell/res/values-cs/strings.xml
index 4e41b79..f05a9ba 100644
--- a/packages/Shell/res/values-cs/strings.xml
+++ b/packages/Shell/res/values-cs/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Zpráva o chybě <xliff:g id="ID">#%d</xliff:g> byla vytvořena"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Přidávání podrobností do zprávy o chybě"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Čekejte prosím…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Chcete-li hlášení chyby sdílet, přejeďte doleva."</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Na telefonu se brzy zobrazí zpráva o chybě."</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Zprávu o chybě můžete sdílet klepnutím"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Klepnutím můžete zprávu o chybě sdílet bez snímku obrazovky, nebo vyčkejte, než se snímek připraví"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Klepnutím můžete zprávu o chybě sdílet bez snímku obrazovky, nebo vyčkejte, než se snímek připraví"</string>
diff --git a/packages/Shell/res/values-da/strings.xml b/packages/Shell/res/values-da/strings.xml
index 19e800bf..3675eeb 100644
--- a/packages/Shell/res/values-da/strings.xml
+++ b/packages/Shell/res/values-da/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Fejrapporten <xliff:g id="ID">#%d</xliff:g> blev gemt"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Tilføjelse af oplysninger til fejlrapporten"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Vent et øjeblik…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Stryg til venstre for at dele din fejlrapport"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Fejlrapporten vises på telefonen om et øjeblik"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Tryk for at dele din fejlrapport"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Tryk for at dele din fejlrapport uden et skærmbillede, eller vent på, at skærmbilledet fuldføres"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Tryk for at dele din fejlrapport uden et skærmbillede, eller vent på, at skærmbilledet fuldføres"</string>
diff --git a/packages/Shell/res/values-de/strings.xml b/packages/Shell/res/values-de/strings.xml
index 0235824..543ccdd 100644
--- a/packages/Shell/res/values-de/strings.xml
+++ b/packages/Shell/res/values-de/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Fehlerbericht <xliff:g id="ID">#%d</xliff:g> erfasst"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Informationen werden zum Fehlerbericht hinzugefügt"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Bitte warten…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Wische nach links, um deinen Fehlerbericht zu teilen."</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Der Fehlerbericht wird in Kürze auf dem Smartphone angezeigt"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Zum Teilen des Fehlerberichts tippen"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Tippe, um den Fehlerbericht ohne Screenshot zu teilen, oder warte auf den Screenshot"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Tippe, um den Fehlerbericht ohne Screenshot zu teilen, oder warte auf den Screenshot"</string>
diff --git a/packages/Shell/res/values-el/strings.xml b/packages/Shell/res/values-el/strings.xml
index 4bed10c..a0222d8 100644
--- a/packages/Shell/res/values-el/strings.xml
+++ b/packages/Shell/res/values-el/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Έγινε λήψη της αναφοράς σφάλματος <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Προσθήκη λεπτομερειών στην αναφορά σφάλματος"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Περιμένετε…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Σύρετε προς τα αριστερά για κοινή χρήση της αναφοράς σφαλμάτων"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Η αναφορά σφαλμάτων θα εμφανιστεί σύντομα στο τηλέφωνο"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Πατήστε για κοινή χρήση της αναφοράς σφάλματος"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Πατήστε για κοινοποίηση της αναφοράς σφάλματος χωρίς στιγμιότυπο οθόνης ή περιμένετε να ολοκληρωθεί"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Πατήστε για κοινοποίηση της αναφοράς σφάλματος χωρίς στιγμιότυπο οθόνης ή περιμένετε να ολοκληρωθεί"</string>
diff --git a/packages/Shell/res/values-en-rAU/strings.xml b/packages/Shell/res/values-en-rAU/strings.xml
index 15b587b..55cfd60 100644
--- a/packages/Shell/res/values-en-rAU/strings.xml
+++ b/packages/Shell/res/values-en-rAU/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Bug report <xliff:g id="ID">#%d</xliff:g> captured"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Adding details to the bug report"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Please wait…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Swipe left to share your bug report"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"The bug report will appear on the phone shortly"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Tap to share your bug report"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Tap to share your bug report without a screenshot or wait for the screenshot to finish"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Tap to share your bug report without a screenshot or wait for the screenshot to finish"</string>
diff --git a/packages/Shell/res/values-en-rGB/strings.xml b/packages/Shell/res/values-en-rGB/strings.xml
index 15b587b..55cfd60 100644
--- a/packages/Shell/res/values-en-rGB/strings.xml
+++ b/packages/Shell/res/values-en-rGB/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Bug report <xliff:g id="ID">#%d</xliff:g> captured"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Adding details to the bug report"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Please wait…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Swipe left to share your bug report"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"The bug report will appear on the phone shortly"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Tap to share your bug report"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Tap to share your bug report without a screenshot or wait for the screenshot to finish"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Tap to share your bug report without a screenshot or wait for the screenshot to finish"</string>
diff --git a/packages/Shell/res/values-en-rIN/strings.xml b/packages/Shell/res/values-en-rIN/strings.xml
index 15b587b..55cfd60 100644
--- a/packages/Shell/res/values-en-rIN/strings.xml
+++ b/packages/Shell/res/values-en-rIN/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Bug report <xliff:g id="ID">#%d</xliff:g> captured"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Adding details to the bug report"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Please wait…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Swipe left to share your bug report"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"The bug report will appear on the phone shortly"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Tap to share your bug report"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Tap to share your bug report without a screenshot or wait for the screenshot to finish"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Tap to share your bug report without a screenshot or wait for the screenshot to finish"</string>
diff --git a/packages/Shell/res/values-es-rUS/strings.xml b/packages/Shell/res/values-es-rUS/strings.xml
index b7398db..dac7fdb 100644
--- a/packages/Shell/res/values-es-rUS/strings.xml
+++ b/packages/Shell/res/values-es-rUS/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Se capturó el informe de errores <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Agregando detalles al informe de errores"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Espera…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Desliza el dedo hacia la izquierda para compartir el informe de errores."</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"El informe de errores aparecerá en el teléfono en breve"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Toca para compartir el informe de errores"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Toca para compartir tu informe de errores sin una captura de pantalla o espera a que finalice"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Toca para compartir tu informe de errores sin una captura de pantalla o espera a que finalice"</string>
diff --git a/packages/Shell/res/values-es/strings.xml b/packages/Shell/res/values-es/strings.xml
index 81d8078..b090b8a3 100644
--- a/packages/Shell/res/values-es/strings.xml
+++ b/packages/Shell/res/values-es/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Informe de errores <xliff:g id="ID">#%d</xliff:g> capturado"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Añadiendo detalles al informe de errores"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Espera…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Desliza el dedo hacia la izquierda para compartir el informe de error"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"El informe de errores aparecerá en el teléfono en breve"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Toca para compartir el informe de errores"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Toca para compartir el informe de errores sin captura de pantalla o espera a que se haga la captura."</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Toca para compartir el informe de errores sin captura de pantalla o espera a que se haga la captura."</string>
diff --git a/packages/Shell/res/values-et-rEE/strings.xml b/packages/Shell/res/values-et-rEE/strings.xml
index edf1c09..aaae44b 100644
--- a/packages/Shell/res/values-et-rEE/strings.xml
+++ b/packages/Shell/res/values-et-rEE/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Jäädvustati veaaruanne <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Üksikasjade lisamine veaaruandesse"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Oodake …"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Veaaruande jagamiseks pühkige vasakule"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Veaaruanne kuvatakse telefonis peagi"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Veaaruande jagamiseks puudutage"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Puudutage, et veaaruannet ilma ekraanipildita jagada, või oodake, kuni ekraanipilt tehtud saab."</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Puudutage, et veaaruannet ilma ekraanipildita jagada, või oodake, kuni ekraanipilt tehtud saab."</string>
diff --git a/packages/Shell/res/values-eu-rES/strings.xml b/packages/Shell/res/values-eu-rES/strings.xml
index 1a220ea..f60e589 100644
--- a/packages/Shell/res/values-eu-rES/strings.xml
+++ b/packages/Shell/res/values-eu-rES/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Akatsen <xliff:g id="ID">#%d</xliff:g> txostena egin da"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Akatsen txostenean xehetasunak gehitzen"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Itxaron, mesedez…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Programa-akatsen txostena partekatzeko, pasatu hatza ezkerrera"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Akatsen txostena telefonoan agertuko da laster"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Sakatu akatsen txostena partekatzeko"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Sakatu akatsen txostena argazkirik gabe partekatzeko edo itxaron pantaila-argazkia atera arte"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Sakatu akatsen txostena argazkirik gabe partekatzeko edo itxaron pantaila-argazkia atera arte"</string>
diff --git a/packages/Shell/res/values-fa/strings.xml b/packages/Shell/res/values-fa/strings.xml
index 0a5b84e..f3e92d2 100644
--- a/packages/Shell/res/values-fa/strings.xml
+++ b/packages/Shell/res/values-fa/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"گزارش اشکال <xliff:g id="ID">#%d</xliff:g> ثبت شد"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"اضافه کردن جزئیات به گزارش اشکال"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"لطفاً منتظر بمانید..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"برای اشتراکگذاری گزارش اشکال، به تندی آن را به چپ بکشید"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"گزارش مشکل بهزودی در تلفن نشان داده میشود"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"برای به اشتراک گذاشتن گزارش اشکال، ضربه بزنید"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"برای اشتراکگذاری گزارش مشکل بدون عکس صفحهنمایش، ضربه بزنید یا صبر کنید تا عکس صفحهنمایش گرفته شود."</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"برای اشتراکگذاری گزارش مشکل بدون عکس صفحهنمایش، ضربه بزنید یا صبر کنید تا عکس صفحهنمایش گرفته شود."</string>
diff --git a/packages/Shell/res/values-fi/strings.xml b/packages/Shell/res/values-fi/strings.xml
index 894217e..a790cd9 100644
--- a/packages/Shell/res/values-fi/strings.xml
+++ b/packages/Shell/res/values-fi/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Virheraportti <xliff:g id="ID">#%d</xliff:g> tallennettu"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Lisätään tietoja virheraporttiin"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Odota…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Jaa virheraportti pyyhkäisemällä vasemmalle"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Virheraportti näkyy puhelimessa pian."</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Jaa virheraportti napauttamalla."</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Jaa virheraportti ilman kuvakaappausta napauttamalla tai odota, että kuvakaappaus latautuu."</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Jaa virheraportti ilman kuvakaappausta napauttamalla tai odota, että kuvakaappaus latautuu."</string>
diff --git a/packages/Shell/res/values-fr-rCA/strings.xml b/packages/Shell/res/values-fr-rCA/strings.xml
index 495d26d..baacdd1 100644
--- a/packages/Shell/res/values-fr-rCA/strings.xml
+++ b/packages/Shell/res/values-fr-rCA/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Rapport de bogue <xliff:g id="ID">#%d</xliff:g> enregistré"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Ajout de détails au rapport de bogue"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Veuillez patienter…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Faites glisser le doigt vers la gauche pour partager votre rapport de bogue."</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Le rapport de bogue s\'affichera bientôt sur le téléphone"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Touchez ici pour partager votre rapport de bogue"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Touchez pour partager le rapport de bogue sans saisie d\'écran ou attendez que la saisie soit prête"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Touchez pour partager le rapport de bogue sans saisie d\'écran ou attendez que la saisie soit prête"</string>
diff --git a/packages/Shell/res/values-fr/strings.xml b/packages/Shell/res/values-fr/strings.xml
index 0465916..506cd68f9 100644
--- a/packages/Shell/res/values-fr/strings.xml
+++ b/packages/Shell/res/values-fr/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Le rapport de bug \"<xliff:g id="ID">#%d</xliff:g>\" a bien été enregistré"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Ajout d\'informations au rapport de bug"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Veuillez patienter…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Faites glisser le doigt vers la gauche pour partager votre rapport d\'erreur."</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Le rapport de bug s\'affichera bientôt sur le téléphone."</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Appuyer pour partager votre rapport de bug"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Appuyer pour partager rapport de bug sans capture d\'écran ou attendre finalisation capture d\'écran"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Appuyer pour partager rapport de bug sans capture d\'écran ou attendre finalisation capture d\'écran"</string>
diff --git a/packages/Shell/res/values-gl-rES/strings.xml b/packages/Shell/res/values-gl-rES/strings.xml
index a06a49b..d2a39fd 100644
--- a/packages/Shell/res/values-gl-rES/strings.xml
+++ b/packages/Shell/res/values-gl-rES/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Rexistrouse o informe de erros <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Engadindo detalles ao informe de erro"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Agarda..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Pasa o dedo á esquerda para compartir o teu informe de erros"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"O informe de erros aparecerá no teléfono en breve"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Toca para compartir o teu informe de erros"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Toca para compartir o informe de erros sen captura de pantalla ou agarda a que finalice a captura"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Toca para compartir o informe de erros sen captura de pantalla ou agarda a que finalice a captura"</string>
diff --git a/packages/Shell/res/values-gu-rIN/strings.xml b/packages/Shell/res/values-gu-rIN/strings.xml
index 83a0ab6..849f1ea 100644
--- a/packages/Shell/res/values-gu-rIN/strings.xml
+++ b/packages/Shell/res/values-gu-rIN/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"બગ રિપોર્ટ <xliff:g id="ID">#%d</xliff:g> કૅપ્ચર કરી"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"બગ રિપોર્ટમાં વિગતો ઉમેરવી"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"કૃપા કરીને રાહ જુઓ…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"તમારી બગ રિપોર્ટ શેર કરવા માટે ડાબે સ્વાઇપ કરો"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"બગ રિપોર્ટ ટૂંક સમયમાં ફોન પર દેખાશે"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"તમારી બગ રિપોર્ટ શેર કરવા ટૅપ કરો"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"સ્ક્રીનશૉટ વગર અથવા સ્ક્રીનશૉટ સમાપ્ત થવાની રાહ જોયા વગર તમારી બગ રિપોર્ટ શેર કરવા ટૅપ કરો"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"સ્ક્રીનશૉટ વગર અથવા સ્ક્રીનશૉટ સમાપ્ત થવાની રાહ જોયા વગર તમારી બગ રિપોર્ટ શેર કરવા ટૅપ કરો"</string>
diff --git a/packages/Shell/res/values-hi/strings.xml b/packages/Shell/res/values-hi/strings.xml
index 9086d89..fc314af 100644
--- a/packages/Shell/res/values-hi/strings.xml
+++ b/packages/Shell/res/values-hi/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"बग रिपोर्ट <xliff:g id="ID">#%d</xliff:g> कैप्चर की गई"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"बग रिपोर्ट में विवरण जोड़े जा रहे हैं"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"कृपया प्रतीक्षा करें…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"अपनी बग रिपोर्ट साझा करने के लिए बाएं स्वाइप करें"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"बग रिपोर्ट थोड़ी ही देर में फ़ोन पर दिखाई देगी"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"अपनी बग रिपोर्ट साझा करने के लिए टैप करें"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"अपनी बग रिपोर्ट को बिना स्क्रीनशॉट साझा करने हेतु टैप करें या स्क्रीनशॉट पूरा होने की प्रतीक्षा करें"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"अपनी बग रिपोर्ट को बिना स्क्रीनशॉट साझा करने हेतु टैप करें या स्क्रीनशॉट पूरा होने की प्रतीक्षा करें"</string>
diff --git a/packages/Shell/res/values-hr/strings.xml b/packages/Shell/res/values-hr/strings.xml
index 24b657b..ed48fd9 100644
--- a/packages/Shell/res/values-hr/strings.xml
+++ b/packages/Shell/res/values-hr/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Izvješće o programskoj pogrešci <xliff:g id="ID">#%d</xliff:g> snimljeno"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Dodavanje pojedinosti u izvješće o progr. pogrešci"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Pričekajte..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Prijeđite prstom ulijevo da biste poslali izvješće o programskim pogreškama"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Izvješće o programskoj pogrešci uskoro će se pojaviti na telefonu"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Dodirnite da biste podijelili izvješće o programskoj pogrešci"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Dodirnite za dijeljenje izvješća o pogrešci bez snimke zaslona ili pričekajte da se izradi snimka"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Dodirnite za dijeljenje izvješća o pogrešci bez snimke zaslona ili pričekajte da se izradi snimka"</string>
diff --git a/packages/Shell/res/values-hu/strings.xml b/packages/Shell/res/values-hu/strings.xml
index a8c5c76..b7def06 100644
--- a/packages/Shell/res/values-hu/strings.xml
+++ b/packages/Shell/res/values-hu/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Hibajelentés (<xliff:g id="ID">#%d</xliff:g>) rögzítve"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Információk hozzáadása a hibajelentéshez"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Kérjük, várjon..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Húzza ujját balra a hibajelentés megosztásához"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"A hibajelentés hamarosan megjelenik a telefonon."</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Koppintson a hibajelentés megosztásához"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Koppintson ide, ha képernyőkép nélkül osztaná meg a hibajelentést, vagy várjon a képernyőképre."</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Koppintson ide, ha képernyőkép nélkül osztaná meg a hibajelentést, vagy várjon a képernyőképre."</string>
diff --git a/packages/Shell/res/values-hy-rAM/strings.xml b/packages/Shell/res/values-hy-rAM/strings.xml
index 56627f4..27e25c6 100644
--- a/packages/Shell/res/values-hy-rAM/strings.xml
+++ b/packages/Shell/res/values-hy-rAM/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"<xliff:g id="ID">#%d</xliff:g> վրիպակի զեկույցը գրանցվեց"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Տվյալների ավելացում վրիպակի զեկույցում"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Խնդրում ենք սպասել…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Սահեցրեք ձախ՝ սխալի հաշվետվությունը համօգտագործելու համար"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Վրիպակների մասին հաշվետվությունը շուտով կստանաք հեռախոսին"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Հպեք՝ վրիպակի զեկույցը տրամադրելու համար"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Հպեք՝ վրիպակի զեկույցն առանց էկրանի պատկերի ուղարկելու համար կամ սպասեք էկրանի պատկերի ստեղծմանը"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Հպեք՝ վրիպակի զեկույցն առանց էկրանի պատկերի ուղարկելու համար կամ սպասեք էկրանի պատկերի ստեղծմանը"</string>
diff --git a/packages/Shell/res/values-in/strings.xml b/packages/Shell/res/values-in/strings.xml
index 90700c7..d4851e3 100644
--- a/packages/Shell/res/values-in/strings.xml
+++ b/packages/Shell/res/values-in/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Laporan bug <xliff:g id="ID">#%d</xliff:g> dijepret"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Menambahkan detail ke laporan bug"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Harap tunggu..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Gesek ke kiri untuk membagikan laporan bug Anda"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Laporan bug akan segera muncul di ponsel"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Ketuk untuk membagikan laporan bug"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Ketuk untuk membagikan laporan bug tanpa tangkapan layar atau menunggu tangkapan layar selesai"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Ketuk untuk membagikan laporan bug tanpa tangkapan layar atau menunggu tangkapan layar selesai"</string>
diff --git a/packages/Shell/res/values-is-rIS/strings.xml b/packages/Shell/res/values-is-rIS/strings.xml
index dc21b35..872c029 100644
--- a/packages/Shell/res/values-is-rIS/strings.xml
+++ b/packages/Shell/res/values-is-rIS/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Villutilkynning <xliff:g id="ID">#%d</xliff:g> búin til"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Bætir upplýsingum við villutilkynningu"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Augnablik..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Strjúktu til vinstri til að deila villuskýrslunni"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Villuskýrslan birtist brátt í símanum"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Ýttu til að deila villutilkynningunni"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Ýttu til að deila villutilkynningunni án skjámyndar eða hinkraðu þangað til skjámyndin er tilbúin"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Ýttu til að deila villutilkynningunni án skjámyndar eða hinkraðu þangað til skjámyndin er tilbúin"</string>
diff --git a/packages/Shell/res/values-it/strings.xml b/packages/Shell/res/values-it/strings.xml
index 0fd6c7a..d1e7c09 100644
--- a/packages/Shell/res/values-it/strings.xml
+++ b/packages/Shell/res/values-it/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Segnalazione di bug <xliff:g id="ID">#%d</xliff:g> acquisita"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Aggiunta di dettagli alla segnalazione di bug"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Attendi..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Scorri verso sinistra per condividere il rapporto sui bug"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"La segnalazione di bug comparirà a breve sul telefono"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Tocca per condividere la segnalazione di bug"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Tocca per inviare la segnalazione del bug senza screenshot o attendi che lo screenshot sia completo"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Tocca per inviare la segnalazione del bug senza screenshot o attendi che lo screenshot sia completo"</string>
diff --git a/packages/Shell/res/values-iw/strings.xml b/packages/Shell/res/values-iw/strings.xml
index b3f9d02..be4b190 100644
--- a/packages/Shell/res/values-iw/strings.xml
+++ b/packages/Shell/res/values-iw/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"הדוח על הבאג <xliff:g id="ID">#%d</xliff:g> צולם"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"מוסיף פרטים לדוח על הבאג"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"המתן…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"החלק שמאלה כדי לשתף את דוח הבאגים"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"הדוח על הבאג יופיע בטלפון בקרוב"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"הקש כדי לשתף את הדוח על הבאג"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"הקש כדי לשתף את הדוח על הבאג ללא צילום מסך, או המתן להשלמת צילום המסך"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"הקש כדי לשתף את הדוח על הבאג ללא צילום מסך, או המתן להשלמת צילום המסך"</string>
diff --git a/packages/Shell/res/values-ja/strings.xml b/packages/Shell/res/values-ja/strings.xml
index eca0ea0..c6682d9 100644
--- a/packages/Shell/res/values-ja/strings.xml
+++ b/packages/Shell/res/values-ja/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"バグレポート <xliff:g id="ID">#%d</xliff:g> の記録完了"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"バグレポートに詳細情報を追加しています"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"お待ちください…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"バグレポートを共有するには左にスワイプ"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"バグレポートはまもなくスマートフォンに表示されます"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"バグレポートを共有するにはタップします"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"タップしてバグレポートをスクリーンショットなしで共有するか、スクリーンショット完成までお待ちください"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"タップしてバグレポートをスクリーンショットなしで共有するか、スクリーンショット完成までお待ちください"</string>
diff --git a/packages/Shell/res/values-ka-rGE/strings.xml b/packages/Shell/res/values-ka-rGE/strings.xml
index 43cdeee..f97f94a 100644
--- a/packages/Shell/res/values-ka-rGE/strings.xml
+++ b/packages/Shell/res/values-ka-rGE/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"ხარვეზების შესახებ ანგარიში <xliff:g id="ID">#%d</xliff:g> აღბეჭდილია"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"ხარვეზის შესახებ ანგარიშს დეტალები ემატება"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"გთხოვთ, მოითმინოთ..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"გაასრიალეთ მარცხნივ თქვენი ხარვეზის შეტყობინების გასაზიარებლად"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"ხარვეზის შესახებ ანგარიში ტელეფონის ეკრანზე მალე გამოჩნდება."</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"შეეხეთ ხარვეზების შესახებ ანგარიშის გასაზიარებლად"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"შეეხეთ ხარვეზის შესახებ ანგარიშის ეკრანის ანაბეჭდის გარეშე გასაზიარებლად, ან დაელოდეთ მის შექმნას"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"შეეხეთ ხარვეზის შესახებ ანგარიშის ეკრანის ანაბეჭდის გარეშე გასაზიარებლად, ან დაელოდეთ მის შექმნას"</string>
diff --git a/packages/Shell/res/values-kk-rKZ/strings.xml b/packages/Shell/res/values-kk-rKZ/strings.xml
index 7d1218f..3d49f4c 100644
--- a/packages/Shell/res/values-kk-rKZ/strings.xml
+++ b/packages/Shell/res/values-kk-rKZ/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"<xliff:g id="ID">#%d</xliff:g> қате туралы есебі жазып алынды"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Қате туралы есепке мәліметтер қосылуда"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Күте тұрыңыз…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Қате туралы есепті бөлісу үшін солға жанаңыз"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Көп ұзамай қате туралы есеп телефон экранына шығады"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Қате туралы есепті бөлісу үшін түртіңіз"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Қате туралы есепті скриншотсыз бөлісу үшін түртіңіз немесе скриншот сақталып болғанша күтіңіз"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Қате туралы есепті скриншотсыз бөлісу үшін түртіңіз немесе скриншот сақталып болғанша күтіңіз"</string>
diff --git a/packages/Shell/res/values-km-rKH/strings.xml b/packages/Shell/res/values-km-rKH/strings.xml
index c9633db7f4..a73cfaa 100644
--- a/packages/Shell/res/values-km-rKH/strings.xml
+++ b/packages/Shell/res/values-km-rKH/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"<xliff:g id="ID">#%d</xliff:g> របាយការណ៍កំហុសត្រូវបានថត"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"កំពុងបន្ថែមព័ត៌មានលម្អិតទៅរបាយការណ៍កំហុស"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"សូមរង់ចាំ…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"អូសទៅឆ្វេង ដើម្បីចែករំលែករបាយការណ៍កំហុសរបស់អ្នក"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"របាយការណ៍កំហុសនេះនឹងបង្ហាញនៅលើទូរស័ព្ទរបស់អ្នកនាពេលបន្តិចទៀតនេះ"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"ប៉ះដើម្បីចែករំលែករបាយការណ៍កំហុសរបស់អ្នក"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"ប៉ះដើម្បីចែករំលែករបាយការណ៍កំហុសរបស់អ្នកដោយមិនចាំបាច់មានរូបថតអេក្រង់ ឬរង់ចាំការបញ្ចប់ការថតអេក្រង់"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"ប៉ះដើម្បីចែករំលែករបាយការណ៍កំហុសរបស់អ្នកដោយមិនចាំបាច់មានរូបថតអេក្រង់ ឬរង់ចាំការបញ្ចប់ការថតអេក្រង់"</string>
diff --git a/packages/Shell/res/values-kn-rIN/strings.xml b/packages/Shell/res/values-kn-rIN/strings.xml
index fc8d03c..117400a 100644
--- a/packages/Shell/res/values-kn-rIN/strings.xml
+++ b/packages/Shell/res/values-kn-rIN/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"ದೋಷ ವರದಿಯ <xliff:g id="ID">#%d</xliff:g> ಅನ್ನು ಕ್ಯಾಪ್ಚರ್ ಮಾಡಿಕೊಳ್ಳಲಾಗಿದೆ"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"ಬಗ್ ವರದಿಗೆ ವಿವರಗಳನ್ನು ಸೇರಿಸಲಾಗುತ್ತಿದೆ"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"ದಯವಿಟ್ಟು ನಿರೀಕ್ಷಿಸಿ..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"ನಿಮ್ಮ ದೋಷ ವರದಿಯನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ಎಡಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"ಬಗ್ ವರದಿ ಶೀಘ್ರದಲ್ಲೇ ಫೋನ್ನಲ್ಲಿ ಗೋಚರಿಸಲಿದೆ"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"ನಿಮ್ಮ ಬಗ್ ವರದಿಯನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಇಲ್ಲದೇ ನಿಮ್ಮ ಬಗ್ ವರದಿಯನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ಟ್ಯಾಪ್ ಮಾಡಿ ಅಥವಾ ಸ್ಕ್ರೀನ್ಶಾಟ್ ಪೂರ್ತಿಯಾಗುವವರೆಗೂ ನಿರೀಕ್ಷಿಸಿ"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಇಲ್ಲದೇ ನಿಮ್ಮ ಬಗ್ ವರದಿಯನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ಟ್ಯಾಪ್ ಮಾಡಿ ಅಥವಾ ಸ್ಕ್ರೀನ್ಶಾಟ್ ಪೂರ್ತಿಯಾಗುವವರೆಗೂ ನಿರೀಕ್ಷಿಸಿ"</string>
diff --git a/packages/Shell/res/values-ko/strings.xml b/packages/Shell/res/values-ko/strings.xml
index d382fb1..6fde072 100644
--- a/packages/Shell/res/values-ko/strings.xml
+++ b/packages/Shell/res/values-ko/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"버그 신고 <xliff:g id="ID">#%d</xliff:g> 캡처됨"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"세부정보를 버그 보고서에 추가"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"잠시 기다려 주세요..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"왼쪽으로 스와이프하여 버그 신고서를 공유하세요."</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"곧 버그 보고서가 휴대전화에 표시됩니다."</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"버그 신고를 공유하려면 탭하세요."</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"스크린샷 없이 버그 신고서를 공유하려면 탭하고 그렇지 않으면 스크린샷이 완료될 때까지 기다려 주세요."</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"스크린샷 없이 버그 신고서를 공유하려면 탭하고 그렇지 않으면 스크린샷이 완료될 때까지 기다려 주세요."</string>
diff --git a/packages/Shell/res/values-ky-rKG/strings.xml b/packages/Shell/res/values-ky-rKG/strings.xml
index fe2dde4..2c97277 100644
--- a/packages/Shell/res/values-ky-rKG/strings.xml
+++ b/packages/Shell/res/values-ky-rKG/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Мүчүлүштүк тууралуу билдирүү <xliff:g id="ID">#%d</xliff:g> жаздырылды"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Мүчүлүштүк жөнүндө кабардын чоо-жайы кошулууда"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Күтө туруңуз…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Ката жөнүндө кабар менен бөлүшүү үчүн солго серпип коюңуз"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Мүчүлүштүктөр жөнүндө кабар жакында телефонго чыгат"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Мүчүлүштүк тууралуу билдирүүңүздү бөлүшүү үчүн таптап коюңуз"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Мүчүлүштүк тууралуу билдирүүңүздү скриншотсуз бөлүшүү үчүн таптап коюңуз же скриншот даяр болгуча күтө туруңуз"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Мүчүлүштүк тууралуу билдирүүңүздү скриншотсуз бөлүшүү үчүн таптап коюңуз же скриншот даяр болгуча күтө туруңуз"</string>
diff --git a/packages/Shell/res/values-lo-rLA/strings.xml b/packages/Shell/res/values-lo-rLA/strings.xml
index 2cc1573..0673c51 100644
--- a/packages/Shell/res/values-lo-rLA/strings.xml
+++ b/packages/Shell/res/values-lo-rLA/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"ບັນທຶກລາຍງານຂໍ້ຜິດພາດ <xliff:g id="ID">#%d</xliff:g> ແລ້ວ"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"ກຳລັງເພີ່ມລາຍລະອຽດໃສ່ລາຍງານຂໍ້ຜິດພາດ"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"ກະລຸນາລໍຖ້າ..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"ປັດໄປຊ້າຍເພື່ອສົ່ງລາຍງານຂໍ້ຜິດພາດຂອງທ່ານ"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"ລາຍງານຂໍ້ຜິດພາດຈະປາກົດໃນໂທລະສັບໃນໄວໆນີ້"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"ແຕະເພື່ອແບ່ງປັນລາຍງານຂໍ້ຜິດພາດຂອງທ່ານ"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"ແຕະເພື່ອແບ່ງປັນລາຍງານຂໍ້ຜິດພາດຂອງທ່ານໂດຍບໍ່ໃຊ້ຮູບໜ້າຈໍ ຫຼື ລໍຖ້າໃຫ້ຮູບໜ້າຈໍແລ້ວໆ"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"ແຕະເພື່ອແບ່ງປັນລາຍງານຂໍ້ຜິດພາດຂອງທ່ານໂດຍບໍ່ໃຊ້ຮູບໜ້າຈໍ ຫຼື ລໍຖ້າໃຫ້ຮູບໜ້າຈໍແລ້ວໆ"</string>
diff --git a/packages/Shell/res/values-lt/strings.xml b/packages/Shell/res/values-lt/strings.xml
index 23fba5f..8d7f0de 100644
--- a/packages/Shell/res/values-lt/strings.xml
+++ b/packages/Shell/res/values-lt/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Pranešimas apie riktą (<xliff:g id="ID">#%d</xliff:g>) užfiksuotas"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Pridedama informacijos prie pranešimo apie riktą"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Palaukite…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Perbraukite kairėn, kad bendrintumėte rikto ataskaitą"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Pranešimai apie riktus netrukus bus rodomi telefone"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Palieskite, kad bendrintumėte pranešimą apie riktą"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Palieskite ir bendrinkite pranešimą apie riktą be ekrano kopijos arba palaukite, kol ji bus sukurta"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Palieskite ir bendrinkite pranešimą apie riktą be ekrano kopijos arba palaukite, kol ji bus sukurta"</string>
diff --git a/packages/Shell/res/values-lv/strings.xml b/packages/Shell/res/values-lv/strings.xml
index d6bfee3..f2258a5 100644
--- a/packages/Shell/res/values-lv/strings.xml
+++ b/packages/Shell/res/values-lv/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Kļūdas pārskats <xliff:g id="ID">#%d</xliff:g> reģistrēts"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Informācijas pievienošana kļūdas pārskatam"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Lūdzu, uzgaidiet..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Velciet pa kreisi, lai kopīgotu savu kļūdu ziņojumu."</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Tālrunī pēc brīža būs redzams kļūdu pārskats"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Pieskarieties, lai kopīgotu kļūdas pārskatu."</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Pieskarieties, lai kopīgotu kļūdas pārskatu bez ekrānuzņēmuma vai gaidiet ekrānuzņēmumu."</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Pieskarieties, lai kopīgotu kļūdas pārskatu bez ekrānuzņēmuma vai gaidiet ekrānuzņēmumu."</string>
diff --git a/packages/Shell/res/values-mk-rMK/strings.xml b/packages/Shell/res/values-mk-rMK/strings.xml
index 3ce9436..f90f4ea 100644
--- a/packages/Shell/res/values-mk-rMK/strings.xml
+++ b/packages/Shell/res/values-mk-rMK/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Извештајот за грешки <xliff:g id="ID">#%d</xliff:g> е снимен"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Се додаваат детали на извештајот за грешка"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Почекајте..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Повлечете налево за да споделите пријава за грешка"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Извештајот за грешки наскоро ќе се појави на телефонот"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Допрете за да го споделите извештајот за грешки"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Допрете за споделување извештај за грешки без слика од екранот или почекајте да се подготви сликата"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Допрете за споделување извештај за грешки без слика од екранот или почекајте да се подготви сликата"</string>
diff --git a/packages/Shell/res/values-ml-rIN/strings.xml b/packages/Shell/res/values-ml-rIN/strings.xml
index 13d6ee4..930f723 100644
--- a/packages/Shell/res/values-ml-rIN/strings.xml
+++ b/packages/Shell/res/values-ml-rIN/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"ബഗ് റിപ്പോർട്ട് <xliff:g id="ID">#%d</xliff:g> ക്യാപ്ചർ ചെയ്തു"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"ബഗ് റിപ്പോർട്ടിലേക്ക് വിശദാംശങ്ങൾ ചേർക്കുന്നു"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"കാത്തിരിക്കുക..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"നിങ്ങളുടെ ബഗ് റിപ്പോർട്ട് പങ്കിടുന്നതിന് ഇടത്തേയ്ക്ക് സ്വൈപ്പുചെയ്യുക"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"ബഗ് റിപ്പോർട്ട് താമസിയാതെ ഫോണിൽ ദൃശ്യമാകും"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"നിങ്ങളുടെ ബഗ് റിപ്പോർട്ട് പങ്കിടാൻ ടാപ്പുചെയ്യുക"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"സ്ക്രീൻഷോട്ട് കൂടാതെയോ സ്ക്രീൻഷോട്ട് പൂർത്തിയാകുന്നതിന് കാക്കാതെയോ നിങ്ങളുടെ ബഗ് റിപ്പോർട്ട് പങ്കിടാൻ ടാപ്പുചെയ്യുക"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"സ്ക്രീൻഷോട്ട് കൂടാതെയോ സ്ക്രീൻഷോട്ട് പൂർത്തിയാകുന്നതിന് കാക്കാതെയോ നിങ്ങളുടെ ബഗ് റിപ്പോർട്ട് പങ്കിടാൻ ടാപ്പുചെയ്യുക"</string>
diff --git a/packages/Shell/res/values-mn-rMN/strings.xml b/packages/Shell/res/values-mn-rMN/strings.xml
index 591e542..6bde789 100644
--- a/packages/Shell/res/values-mn-rMN/strings.xml
+++ b/packages/Shell/res/values-mn-rMN/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Програмд гарсан алдааны мэдээллийн <xliff:g id="ID">#%d</xliff:g>-г бүртгэгдлээ"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Алдааны тайланд дэлгэрэнгүй мэдээлэл нэмж байна"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Түр хүлээнэ үү..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Өөрийн согог репортыг хуваалцахын тулд зүүн шудрана уу"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Утсанд удахгүй алдааны тайлан гарч ирэх болно"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Програмд гарсан алдааны мэдээллээ хуваалцах бол дарна уу"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Алдааны тайлангаа дэлгэцээс авсан зураггүйгээр хуваалцах бол дарж, эсвэл дэлгэцээс авсан зургийг бэлэн болтол нь хүлээнэ үү"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Алдааны тайлангаа дэлгэцээс авсан зураггүйгээр хуваалцах бол дарж, эсвэл дэлгэцээс авсан зургийг бэлэн болтол нь хүлээнэ үү"</string>
diff --git a/packages/Shell/res/values-mr-rIN/strings.xml b/packages/Shell/res/values-mr-rIN/strings.xml
index 035ac01..084901a 100644
--- a/packages/Shell/res/values-mr-rIN/strings.xml
+++ b/packages/Shell/res/values-mr-rIN/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"दोष अहवाल <xliff:g id="ID">#%d</xliff:g> कॅप्चर केला"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"दोष अहवालामध्ये तपशील जोडत आहे"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"कृपया प्रतीक्षा करा..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"आपला दोष अहवाल सामायिक करण्यासाठी डावीकडे स्वाइप करा"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"फोनवर दोष अहवाल लवकरच दिसेल"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"आपला दोष अहवाल सामायिक करण्यासाठी टॅप करा"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"स्क्रीनशॉट शिवाय आपला दोष अहवाल सामायिक करण्यासाठी टॅप करा किंवा समाप्त करण्यासाठी स्क्रीनशॉटची प्रतीक्षा करा"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"स्क्रीनशॉट शिवाय आपला दोष अहवाल सामायिक करण्यासाठी टॅप करा किंवा समाप्त करण्यासाठी स्क्रीनशॉटची प्रतीक्षा करा"</string>
diff --git a/packages/Shell/res/values-ms-rMY/strings.xml b/packages/Shell/res/values-ms-rMY/strings.xml
index 085152f..36e70c5 100644
--- a/packages/Shell/res/values-ms-rMY/strings.xml
+++ b/packages/Shell/res/values-ms-rMY/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Laporan pepijat <xliff:g id="ID">#%d</xliff:g> telah ditangkap"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Menambahkan butiran pada laporan pepijat"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Sila tunggu…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Leret ke kiri untuk berkongsi laporan pepijat anda"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Laporan pepijat akan dipaparkan pada telefon sebentar lagi"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Ketik untuk berkongsi laporan pepijat anda"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Ketik untuk berkongsi laporan pepijat anda tanpa tangkapan skrin atau tunggu tangkapan skrin selesai"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Ketik untuk berkongsi laporan pepijat anda tanpa tangkapan skrin atau tunggu tangkapan skrin selesai"</string>
diff --git a/packages/Shell/res/values-my-rMM/strings.xml b/packages/Shell/res/values-my-rMM/strings.xml
index c9486c9..ece4a869 100644
--- a/packages/Shell/res/values-my-rMM/strings.xml
+++ b/packages/Shell/res/values-my-rMM/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"ချွတ်ယွင်းမှုအစီရင်ခံချက် <xliff:g id="ID">#%d</xliff:g> ကိုရယူထားပြီးပါပြီ"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"ချွတ်ယွင်းချက်အစီရင်ခံချက်သို့ အသေးစိတ်များပေါင်းထည့်ရန်"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"ခေတ္တစောင့်ပါ..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"သင်၏ ဘာဂ် အစီရင်ခံစာကို မျှပေးရန် ဘယ်ဘက်သို့ ပွတ်ဆွဲရန်"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"ချွတ်ယွင်းချက်အစီရင်ခံစာကို မကြာခင် ဖုန်းထဲတွင် မြင်တွေ့ရပါလိမ့်မည်"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"သင့်ချွတ်ယွင်းမှုအစီရင်ခံချက်ကို မျှဝေရန် တို့ပါ"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"ချွတ်ယွင်းချက်အစီရင်ခံစာကို ဖန်သားပြင်ဓာတ်ပုံမှတ်တမ်းမပါဘဲ မျှဝေရန် တို့ပါ သို့မဟုတ် ဖန်သားပြင်ဓာတ်ပုံမှတ်တမ်းတင်ခြင်း ပြီးဆုံးသည်အထိ စောင့်ပါ"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"ချွတ်ယွင်းချက်အစီရင်ခံစာကို ဖန်သားပြင်ဓာတ်ပုံမှတ်တမ်းမပါဘဲ မျှဝေရန် တို့ပါ သို့မဟုတ် ဖန်သားပြင်ဓာတ်ပုံမှတ်တမ်းတင်ခြင်း ပြီးဆုံးသည်အထိ စောင့်ပါ"</string>
@@ -39,5 +39,5 @@
<string name="bugreport_info_name" msgid="4414036021935139527">"ဖိုင်အမည်"</string>
<string name="bugreport_info_title" msgid="2306030793918239804">"ချွတ်ယွင်းချက် ခေါင်းစဉ်"</string>
<string name="bugreport_info_description" msgid="5072835127481627722">"ချွတ်ယွင်းချက် အကျဉ်းချုပ်"</string>
- <string name="save" msgid="4781509040564835759">"သိမ်းဆည်းပါ"</string>
+ <string name="save" msgid="4781509040564835759">"သိမ်းရန်"</string>
</resources>
diff --git a/packages/Shell/res/values-nb/strings.xml b/packages/Shell/res/values-nb/strings.xml
index 43fb97b..0d5db30 100644
--- a/packages/Shell/res/values-nb/strings.xml
+++ b/packages/Shell/res/values-nb/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Feilrapporten <xliff:g id="ID">#%d</xliff:g> er fullført"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Legger til detaljer i feilrapporten"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Vent litt"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Sveip til venstre for å dele feilrapporten din"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Feilrapporten vises snart på telefonen"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Trykk for å dele feilrapporten"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Trykk for å dele feilrapporten uten noen skjermdump, eller vent til skjermdumpen er klar"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Trykk for å dele feilrapporten uten noen skjermdump, eller vent til skjermdumpen er klar"</string>
diff --git a/packages/Shell/res/values-ne-rNP/strings.xml b/packages/Shell/res/values-ne-rNP/strings.xml
index 4ffa422..d71cb9f 100644
--- a/packages/Shell/res/values-ne-rNP/strings.xml
+++ b/packages/Shell/res/values-ne-rNP/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"बग रिपोर्ट <xliff:g id="ID">#%d</xliff:g>लाई कैद गरियो"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"बग रिपोर्टमा विवरणहरू थप्दै"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"कृपया प्रतीक्षा गर्नुहोला..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"तपाईँको बग रिपोर्ट साझेदारी गर्न बायाँ स्वाइप गर्नुहोस्"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"उक्त बग सम्बन्धी रिपोर्ट चाँडै नै यस फोनमा देखा पर्नेछ"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"तपाईंको बग रिपोर्टलाई साझेदारी गर्न ट्याप गर्नुहोस्"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"तपाईँको बग रिपोर्टलाई स्क्रिनसट बिना साझेदारी गर्नका लागि ट्याप गर्नुहोस् वा स्क्रिनसट लिने प्रक्रिया पूरा हुन प्रतीक्षा गर्नुहोस्"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"तपाईँको बग रिपोर्टलाई स्क्रिनसट बिना साझेदारी गर्नका लागि ट्याप गर्नुहोस् वा स्क्रिनसट लिने प्रक्रिया पूरा हुन प्रतीक्षा गर्नुहोस्"</string>
diff --git a/packages/Shell/res/values-nl/strings.xml b/packages/Shell/res/values-nl/strings.xml
index 1b60f0d..c13ad36 100644
--- a/packages/Shell/res/values-nl/strings.xml
+++ b/packages/Shell/res/values-nl/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Bugrapport <xliff:g id="ID">#%d</xliff:g> is vastgelegd"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Details toevoegen aan het bugrapport"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Even geduld…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Veeg naar links om je bugmelding te delen"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Het bugrapport wordt over enkele ogenblikken op de telefoon weergegeven"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Tik om je bugrapport te delen"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Tik om je bugrapport te delen zonder screenshot of wacht tot het screenshot is voltooid"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Tik om je bugrapport te delen zonder screenshot of wacht tot het screenshot is voltooid"</string>
diff --git a/packages/Shell/res/values-pa-rIN/strings.xml b/packages/Shell/res/values-pa-rIN/strings.xml
index db8b29f..f0bf46e 100644
--- a/packages/Shell/res/values-pa-rIN/strings.xml
+++ b/packages/Shell/res/values-pa-rIN/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"ਬੱਗ ਰਿਪੋਰਟ <xliff:g id="ID">#%d</xliff:g> ਕੈਪਚਰ ਕੀਤੀ ਗਈ"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"ਬੱਗ ਰਿਪੋਰਟ ਵਿੱਚ ਵੇਰਵਿਆਂ ਨੂੰ ਸ਼ਾਮਲ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"ਕਿਰਪਾ ਕਰਕੇ ਉਡੀਕ ਕਰੋ..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"ਤੁਹਾਡੀ ਬਗ ਰਿਪੋਰਟ ਸ਼ੇਅਰ ਕਰਨ ਲਈ ਖੱਬੇ ਪਾਸੇ ਸਵਾਈਪ ਕਰੋ"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"ਬੱਗ ਰਿਪੋਰਟ ਜਲਦ ਹੀ ਫ਼ੋਨ \'ਤੇ ਵਿਖਾਈ ਦੇਵੇਗੀ"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"ਆਪਣੀ ਬੱਗ ਰਿਪੋਰਟ ਸਾਂਝੀ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਦੇ ਬਿਨਾਂ ਆਪਣੀ ਬੱਗ ਰਿਪੋਰਟ ਨੂੰ ਸਾਂਝੀ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ ਜਾਂ ਸਕ੍ਰੀਨਸ਼ਾਟ ਦੇ ਪੂਰੇ ਹੋਣ ਦੀ ਉਡੀਕ ਕਰੋ"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਦੇ ਬਿਨਾਂ ਆਪਣੀ ਬੱਗ ਰਿਪੋਰਟ ਨੂੰ ਸਾਂਝੀ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ ਜਾਂ ਸਕ੍ਰੀਨਸ਼ਾਟ ਦੇ ਪੂਰੇ ਹੋਣ ਦੀ ਉਡੀਕ ਕਰੋ"</string>
diff --git a/packages/Shell/res/values-pl/strings.xml b/packages/Shell/res/values-pl/strings.xml
index d0375b6..9734b2e 100644
--- a/packages/Shell/res/values-pl/strings.xml
+++ b/packages/Shell/res/values-pl/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Raport o błędzie <xliff:g id="ID">#%d</xliff:g> został zapisany"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Dodaję szczegóły do raportu o błędzie"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Czekaj..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Przesuń palcem w lewo, by udostępnić swoje zgłoszenie błędu"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Raport o błędzie będzie pojawiał się na telefonie przez chwilę"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Kliknij, by udostępnić raport o błędzie"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Kliknij, by udostępnić raport o błędzie bez zrzutu ekranu, lub poczekaj, aż zostanie on wygenerowany"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Kliknij, by udostępnić raport o błędzie bez zrzutu ekranu, lub poczekaj, aż zostanie on wygenerowany"</string>
diff --git a/packages/Shell/res/values-pt-rBR/strings.xml b/packages/Shell/res/values-pt-rBR/strings.xml
index 12cf2e4..2947122 100644
--- a/packages/Shell/res/values-pt-rBR/strings.xml
+++ b/packages/Shell/res/values-pt-rBR/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Relatório do bug <xliff:g id="ID">#%d</xliff:g> capturado"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Adicionando detalhes ao relatório do bug"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Aguarde…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Deslize para a esquerda para compartilhar seu relatório de bugs"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"O relatório de bugs será exibido no smartphone em breve"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Toque para compartilhar seu relatório do bug"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Toque para compartilhar seu relatório de bug sem captura de tela ou aguarde a conclusão"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Toque para compartilhar seu relatório de bug sem captura de tela ou aguarde a conclusão"</string>
diff --git a/packages/Shell/res/values-pt-rPT/strings.xml b/packages/Shell/res/values-pt-rPT/strings.xml
index 2148f97..0b08fd6 100644
--- a/packages/Shell/res/values-pt-rPT/strings.xml
+++ b/packages/Shell/res/values-pt-rPT/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Relatório de erro <xliff:g id="ID">#%d</xliff:g> criado"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"A adicionar detalhes ao relatório de erro"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Aguarde..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Deslizar rapidamente para a esquerda para partilhar o seu relatório de erros"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"O relatório de erro será apresentado brevemente no telemóvel"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Toque para partilhar o relatório de erro"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Toque para partilhar o relatório de erro sem uma captura de ecrã ou aguarde a conclusão da mesma"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Toque para partilhar o relatório de erro sem uma captura de ecrã ou aguarde a conclusão da mesma"</string>
diff --git a/packages/Shell/res/values-pt/strings.xml b/packages/Shell/res/values-pt/strings.xml
index 12cf2e4..2947122 100644
--- a/packages/Shell/res/values-pt/strings.xml
+++ b/packages/Shell/res/values-pt/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Relatório do bug <xliff:g id="ID">#%d</xliff:g> capturado"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Adicionando detalhes ao relatório do bug"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Aguarde…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Deslize para a esquerda para compartilhar seu relatório de bugs"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"O relatório de bugs será exibido no smartphone em breve"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Toque para compartilhar seu relatório do bug"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Toque para compartilhar seu relatório de bug sem captura de tela ou aguarde a conclusão"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Toque para compartilhar seu relatório de bug sem captura de tela ou aguarde a conclusão"</string>
diff --git a/packages/Shell/res/values-ro/strings.xml b/packages/Shell/res/values-ro/strings.xml
index 4e57b60..e154563 100644
--- a/packages/Shell/res/values-ro/strings.xml
+++ b/packages/Shell/res/values-ro/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Raportul de eroare <xliff:g id="ID">#%d</xliff:g> a fost creat"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Se adaugă detaliile la raportul de eroare"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Așteptați…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Glisați la stânga pentru a trimite raportul de erori"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Raportul de eroare va apărea curând pe telefon"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Atingeți pentru a trimite raportul de eroare"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Atingeți ca să trimiteți raportul de eroare fără captură de ecran sau așteptați finalizarea acesteia"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Atingeți ca să trimiteți raportul de eroare fără captură de ecran sau așteptați finalizarea acesteia"</string>
diff --git a/packages/Shell/res/values-ru/strings.xml b/packages/Shell/res/values-ru/strings.xml
index 62647ca..9ad590e 100644
--- a/packages/Shell/res/values-ru/strings.xml
+++ b/packages/Shell/res/values-ru/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Отчет об ошибке <xliff:g id="ID">#%d</xliff:g> сохранен"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Добавление данных в отчет об ошибке"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Подождите…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Проведите влево, чтобы отправить отчет"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Отчет об ошибке скоро появится на телефоне"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Нажмите, чтобы отправить отчет об ошибке."</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Нажмите, чтобы отправить отчет об ошибке сразу, или подождите, пока будет сохранен скриншот."</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Нажмите, чтобы отправить отчет об ошибке сразу, или подождите, пока будет сохранен скриншот."</string>
diff --git a/packages/Shell/res/values-si-rLK/strings.xml b/packages/Shell/res/values-si-rLK/strings.xml
index 0b1d478..ecc134e 100644
--- a/packages/Shell/res/values-si-rLK/strings.xml
+++ b/packages/Shell/res/values-si-rLK/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"දෝෂ වාර්තා <xliff:g id="ID">#%d</xliff:g> ග්රහණය කර ගන්නා ලදී"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"දෝෂ වාර්තාව වෙත විස්තර එක් කිරීම"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"කරුණාකර රැඳී සිටින්න..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"ඔබගේ දෝෂ වාර්තාව බෙදාගැනීමට වමට ස්වයිප් කරන්න"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"දෝෂ වාර්තාව ඉක්මනින් දුරකථනය මත දිස් වනු ඇත"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"ඔබගේ දෝෂ වාර්තාව බෙදා ගැනීමට තට්ටු කරන්න"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"තිර රුවක් රහිතව ඔබගේ දෝෂ වාර්තාව බෙදා ගැනීමට තට්ටු කරන්න නැතහොත් තිර රුව ගැනීම අවසන් වන තෙක් රැඳෙන්න"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"තිර රුවක් රහිතව ඔබගේ දෝෂ වාර්තාව බෙදා ගැනීමට තට්ටු කරන්න නැතහොත් තිර රුව ගැනීම අවසන් වන තෙක් රැඳෙන්න"</string>
diff --git a/packages/Shell/res/values-sk/strings.xml b/packages/Shell/res/values-sk/strings.xml
index 3e3a0e4..733287e 100644
--- a/packages/Shell/res/values-sk/strings.xml
+++ b/packages/Shell/res/values-sk/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Hlásenie chyby <xliff:g id="ID">#%d</xliff:g> bolo zaznamenané"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Pridanie podrobností o hlásení chyby"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Čakajte prosím…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Ak chcete hlásenie o chybe zdieľať, prejdite prstom doľava."</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Hlásenie chyby sa čoskoro zobrazí na telefóne"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Hlásenie chyby môžete zdieľať klepnutím"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Klepnutím zdieľajte hlásenie chyby bez snímky obrazovky alebo počkajte na dokončenie snímky obrazovky"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Klepnutím zdieľajte hlásenie chyby bez snímky obrazovky alebo počkajte na dokončenie snímky obrazovky"</string>
diff --git a/packages/Shell/res/values-sl/strings.xml b/packages/Shell/res/values-sl/strings.xml
index 6b07e46..31c944c 100644
--- a/packages/Shell/res/values-sl/strings.xml
+++ b/packages/Shell/res/values-sl/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Poročilo o napaki <xliff:g id="ID">#%d</xliff:g> zajeto"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Dodajanje podrobnosti v poročilo o napakah"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Počakajte ..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Povlecite v levo, če želite poslati sporočilo o napaki"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Poročilo o napakah bo kmalu prikazano v telefonu"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Dotaknite se, če želite poročilo o napaki dati v skupno rabo"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Dotaknite se za pošiljanje poročila o napakah brez posnetka zaslona ali počakajte, da se ta dokonča"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Dotaknite se za pošiljanje poročila o napakah brez posnetka zaslona ali počakajte, da se ta dokonča"</string>
diff --git a/packages/Shell/res/values-sq-rAL/strings.xml b/packages/Shell/res/values-sq-rAL/strings.xml
index 08c77c2..b97c6eb 100644
--- a/packages/Shell/res/values-sq-rAL/strings.xml
+++ b/packages/Shell/res/values-sq-rAL/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Raporti i defekteve në kod <xliff:g id="ID">#%d</xliff:g> u regjistrua"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Po shtohen detajet te raporti i defekteve në kod"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Qëndro në pritje..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Rrëshqit majtas për të ndarë raportin e defektit në kod"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Raporti i defektit nëkod do të shfaqet së shpejti në telefon"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Trokit për të ndarë raportin e defekteve në kod"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Trokit për të ndarë raportin e defekteve në kod pa një pamje çasti ose prit që pamja e çastit të përfundojë"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Trokit për të ndarë raportin e defekteve në kod pa një pamje çasti ose prit që pamja e çastit të përfundojë"</string>
diff --git a/packages/Shell/res/values-sr/strings.xml b/packages/Shell/res/values-sr/strings.xml
index 72a5dc8..c9547c0 100644
--- a/packages/Shell/res/values-sr/strings.xml
+++ b/packages/Shell/res/values-sr/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Извештај о грешци <xliff:g id="ID">#%d</xliff:g> је снимљен"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Додају се детаљи у извештај о грешци"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Сачекајте..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Превуците улево да бисте делили извештај о грешкама"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Извештај о грешци ће се ускоро појавити на телефону"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Додирните да бисте делили извештај о грешци"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Додирните за дељење извештаја о грешци без снимка екрана или сачекајте да се направи снимак екрана"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Додирните за дељење извештаја о грешци без снимка екрана или сачекајте да се направи снимак екрана"</string>
diff --git a/packages/Shell/res/values-sv/strings.xml b/packages/Shell/res/values-sv/strings.xml
index 52ff7c5..dcc38db 100644
--- a/packages/Shell/res/values-sv/strings.xml
+++ b/packages/Shell/res/values-sv/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Felrapporten <xliff:g id="ID">#%d</xliff:g> har skapats"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Lägger till information i felrapporten"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Vänta …"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Svep åt vänster om du vill dela felrapporten"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Felrapporten visas på mobilen inom kort"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Tryck om du vill dela felrapporten"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Tryck om du vill dela felrapporten utan en skärmdump eller vänta tills skärmdumpen är klar"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Tryck om du vill dela felrapporten utan en skärmdump eller vänta tills skärmdumpen är klar"</string>
diff --git a/packages/Shell/res/values-sw/strings.xml b/packages/Shell/res/values-sw/strings.xml
index b31b54f..83f205d 100644
--- a/packages/Shell/res/values-sw/strings.xml
+++ b/packages/Shell/res/values-sw/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Ripoti ya hitilafu ya <xliff:g id="ID">#%d</xliff:g> imerekodiwa"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Inaongeza maelezo kwenye ripoti ya hitilafu"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Tafadhali subiri…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Telezesha kidole kushoto ili ushiriki ripoti yako ya hitilafu"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Tutatuma ripoti ya hitilafu kwenye simu baada ya muda mfupi"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Gonga ili ushiriki ripoti yako ya hitilafu"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Gonga ili ushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Gonga ili ushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
diff --git a/packages/Shell/res/values-ta-rIN/strings.xml b/packages/Shell/res/values-ta-rIN/strings.xml
index bd727ea..2a83db0 100644
--- a/packages/Shell/res/values-ta-rIN/strings.xml
+++ b/packages/Shell/res/values-ta-rIN/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"பிழை அறிக்கை <xliff:g id="ID">#%d</xliff:g> எடுக்கப்பட்டது"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"பிழை அறிக்கையில் விவரங்களைச் சேர்க்கிறது"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"காத்திருக்கவும்…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"பிழை அறிக்கையைப் பகிர இடது புறமாகத் தேய்க்கவும்"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"பிழை அறிக்கை சிறிது நேரத்தில் மொபைலில் தோன்றும்"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"பிழை அறிக்கையைப் பகிர, தட்டவும்"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"ஸ்கிரீன்ஷாட் இல்லாமல் பிழை அறிக்கையைப் பகிர, தட்டவும் அல்லது ஸ்கிரீன்ஷாட் முடியும்வரை காத்திருக்கவும்"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"ஸ்கிரீன்ஷாட் இல்லாமல் பிழை அறிக்கையைப் பகிர, தட்டவும் அல்லது ஸ்கிரீன்ஷாட் முடியும்வரை காத்திருக்கவும்"</string>
diff --git a/packages/Shell/res/values-te-rIN/strings.xml b/packages/Shell/res/values-te-rIN/strings.xml
index a6beb3c..47074fd 100644
--- a/packages/Shell/res/values-te-rIN/strings.xml
+++ b/packages/Shell/res/values-te-rIN/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"బగ్ నివేదిక <xliff:g id="ID">#%d</xliff:g> సంగ్రహించబడింది"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"బగ్ నివేదికకు వివరాలను జోడిస్తోంది"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"దయచేసి వేచి ఉండండి..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"మీ బగ్ నివేదికను భాగస్వామ్యం చేయడానికి ఎడమవైపుకు స్వైప్ చేయండి"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"బగ్ నివేదిక త్వరలో ఫోన్లో కనిపిస్తుంది"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"మీ బగ్ నివేదికను భాగస్వామ్యం చేయడానికి నొక్కండి"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"స్క్రీన్షాట్ లేకుండా మీ బగ్ నివే. భాగ. చేయడానికి నొక్కండి లేదా స్క్రీన్షాట్ ముగిసేదాకా వేచి ఉండండి"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"స్క్రీన్షాట్ లేకుండా మీ బగ్ నివే. భాగ. చేయడానికి నొక్కండి లేదా స్క్రీన్షాట్ ముగిసేదాకా వేచి ఉండండి"</string>
diff --git a/packages/Shell/res/values-th/strings.xml b/packages/Shell/res/values-th/strings.xml
index d76bf12..dddb3b0 100644
--- a/packages/Shell/res/values-th/strings.xml
+++ b/packages/Shell/res/values-th/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"บันทึกรายงานข้อบกพร่อง <xliff:g id="ID">#%d</xliff:g> แล้ว"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"กำลังเพิ่มรายละเอียดในรายงานข้อบกพร่อง"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"โปรดรอสักครู่…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"กวาดไปทางซ้ายเพื่อแชร์รายงานข้อบกพร่อง"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"รายงานข้อบกพร่องจะปรากฏบนโทรศัพท์ในไม่ช้า"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"แตะเพื่อแชร์รายงานข้อบกพร่องของคุณ"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"แตะเพื่อแชร์รายงานข้อบกพร่องของคุณโดยไม่มีภาพหน้าจอ หรือรอให้ภาพหน้าจอเสร็จสมบูรณ์"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"แตะเพื่อแชร์รายงานข้อบกพร่องของคุณโดยไม่มีภาพหน้าจอ หรือรอให้ภาพหน้าจอเสร็จสมบูรณ์"</string>
diff --git a/packages/Shell/res/values-tl/strings.xml b/packages/Shell/res/values-tl/strings.xml
index fc556e1..28805c8 100644
--- a/packages/Shell/res/values-tl/strings.xml
+++ b/packages/Shell/res/values-tl/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Na-capture ang ulat ng bug na <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Pagdaragdag ng mga detalye sa ulat ng bug"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Mangyaring maghintay..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Mag-swipe pakaliwa upang ibahagi ang iyong ulat ng bug"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Lalabas sa telepono ang ulat ng bug pagkalipas ng ilang sandali"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Mag-tap upang ibahagi ang iyong ulat ng bug"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Mag-tap para ibahagi ang iyong ulat ng bug nang walang screenshot o hintaying matapos ang screenshot"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Mag-tap para ibahagi ang iyong ulat ng bug nang walang screenshot o hintaying matapos ang screenshot"</string>
diff --git a/packages/Shell/res/values-tr/strings.xml b/packages/Shell/res/values-tr/strings.xml
index f2b02c3..d444d78 100644
--- a/packages/Shell/res/values-tr/strings.xml
+++ b/packages/Shell/res/values-tr/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Hata raporu (<xliff:g id="ID">#%d</xliff:g>) yakalandı"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Hata raporuna ayrıntılar ekleniyor"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Lütfen bekleyin…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Hata raporunuzu paylaşmak için hızlıca sola kaydırın"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Hata raporu kısa süre içinde telefonda görüntülenecektir"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Hata raporunuzu paylaşmak için hafifçe dokunun"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Hata raporunu ekran görüntüsüz paylaşmak için dokunun veya bitirmek için ekran görüntüsünü bekleyin"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Hata raporunu ekran görüntüsüz paylaşmak için dokunun veya bitirmek için ekran görüntüsünü bekleyin"</string>
diff --git a/packages/Shell/res/values-uk/strings.xml b/packages/Shell/res/values-uk/strings.xml
index 919a2b5..3bb101f 100644
--- a/packages/Shell/res/values-uk/strings.xml
+++ b/packages/Shell/res/values-uk/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Повідомлення про помилку <xliff:g id="ID">#%d</xliff:g> створено"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Додаються деталі до повідомлення про помилку"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Зачекайте…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Проведіть пальцем ліворуч, щоб надіслати звіт про помилки"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Звіт про помилку невдовзі з’явиться на телефоні"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Торкніться, щоб надіслати повідомлення про помилку"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Торкніться, щоб надіслати повідомлення про помилку без знімка екрана або зачекайте на знімок"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Торкніться, щоб надіслати повідомлення про помилку без знімка екрана або зачекайте на знімок"</string>
diff --git a/packages/Shell/res/values-ur-rPK/strings.xml b/packages/Shell/res/values-ur-rPK/strings.xml
index d97a693..3f85bf4 100644
--- a/packages/Shell/res/values-ur-rPK/strings.xml
+++ b/packages/Shell/res/values-ur-rPK/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"بگ رپورٹ <xliff:g id="ID">#%d</xliff:g> کیپچر ہو گئی"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"بگ رپورٹ میں تفصیلات شامل کی جا رہی ہیں"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"براہ کرم انتظار کریں…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"اپنی بگ رپورٹ کا اشتراک کرنے کیلئے بائیں سوائپ کریں"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"بگ رپورٹ فون پر تھوڑی دیر میں ظاہر ہوگی"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"اپنی بگ رپورٹ کا اشتراک کرنے کیلئے تھپتھپائیں"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"بغیر اسکرین شاٹ کے بگ رپورٹ کا اشتراک کرنے کیلئے تھپتھپائیں یا اسکرین شاٹ کے ختم ہونے کا انتظار کریں"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"بغیر اسکرین شاٹ کے بگ رپورٹ کا اشتراک کرنے کیلئے تھپتھپائیں یا اسکرین شاٹ کے ختم ہونے کا انتظار کریں"</string>
diff --git a/packages/Shell/res/values-uz-rUZ/strings.xml b/packages/Shell/res/values-uz-rUZ/strings.xml
index 605fc12..0738ece 100644
--- a/packages/Shell/res/values-uz-rUZ/strings.xml
+++ b/packages/Shell/res/values-uz-rUZ/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Xatoliklar hisoboti (<xliff:g id="ID">#%d</xliff:g>) yozib olindi"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Xatoliklar hisobotiga tafsilotlar qo‘shilmoqda"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Iltimos, kuting…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Xatolik hisobotini yuborish uchun barmog‘ingiz bilan chapga suring"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Xatoliklar hisoboti tez orada telefonda paydo bo‘ladi"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Xatoliklar hisobotini ulashish uchun bosing"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Xatoliklar hisobotini darhol yuborish uchun shu yerga bosing yoki skrinshot saqlanguncha kuting"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Xatoliklar hisobotini darhol yuborish uchun shu yerga bosing yoki skrinshot saqlanguncha kuting"</string>
diff --git a/packages/Shell/res/values-vi/strings.xml b/packages/Shell/res/values-vi/strings.xml
index e9b5a9d..f988801 100644
--- a/packages/Shell/res/values-vi/strings.xml
+++ b/packages/Shell/res/values-vi/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Đã chụp báo cáo lỗi <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Đang thêm thông tin chi tiết vào báo cáo lỗi"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Vui lòng đợi…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Vuốt sang trái để chia sẻ báo cáo lỗi của bạn"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Báo cáo lỗi sẽ xuất hiện ngay trên điện thoại"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Nhấn để chia sẻ báo cáo lỗi của bạn"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Bấm để chia sẻ báo cáo lỗi mà không cần ảnh chụp màn hình hoặc đợi hoàn tất ảnh chụp màn hình"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Bấm để chia sẻ báo cáo lỗi mà không cần ảnh chụp màn hình hoặc đợi hoàn tất ảnh chụp màn hình"</string>
diff --git a/packages/Shell/res/values-zh-rCN/strings.xml b/packages/Shell/res/values-zh-rCN/strings.xml
index ce10d56..a42534c 100644
--- a/packages/Shell/res/values-zh-rCN/strings.xml
+++ b/packages/Shell/res/values-zh-rCN/strings.xml
@@ -18,10 +18,10 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"正在生成错误报告 <xliff:g id="ID">#%d</xliff:g>"</string>
- <string name="bugreport_finished_title" msgid="4429132808670114081">"已捕获错误报告 <xliff:g id="ID">#%d</xliff:g>"</string>
+ <string name="bugreport_finished_title" msgid="4429132808670114081">"已获取错误报告 <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"正在向错误报告添加详细信息"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"请稍候…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"向左滑动即可分享错误报告"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"错误报告将很快显示在手机上"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"点按即可分享您的错误报告"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"点按即可分享不含屏幕截图的错误报告;您也可以等待屏幕截图完成"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"点按即可分享不含屏幕截图的错误报告;您也可以等待屏幕截图完成"</string>
diff --git a/packages/Shell/res/values-zh-rHK/strings.xml b/packages/Shell/res/values-zh-rHK/strings.xml
index 8433d22..6004621 100644
--- a/packages/Shell/res/values-zh-rHK/strings.xml
+++ b/packages/Shell/res/values-zh-rHK/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"已擷取錯誤報告 <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"正在新增錯誤報告詳細資訊"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"請稍候…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"向左滑動即可分享錯誤報告"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"錯誤報告即將在手機上顯示"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"輕按即可分享錯誤報告"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"輕按以分享錯誤報告 (不包含螢幕擷圖),或等待螢幕畫面擷取完成"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"輕按以分享錯誤報告 (不包含螢幕擷圖),或等待螢幕畫面擷取完成"</string>
diff --git a/packages/Shell/res/values-zh-rTW/strings.xml b/packages/Shell/res/values-zh-rTW/strings.xml
index 85dde84..1b7bee3 100644
--- a/packages/Shell/res/values-zh-rTW/strings.xml
+++ b/packages/Shell/res/values-zh-rTW/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"已擷取錯誤報告 <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"正在新增錯誤報告詳細資訊"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"請稍候…"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"向左滑動即可分享錯誤報告"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"系統即將在手機上顯示錯誤報告"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"輕觸即可分享錯誤報告"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"輕觸即可分享無螢幕擷圖的錯誤報告;您也可以等候螢幕畫面擷取完畢"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"輕觸即可分享無螢幕擷圖的錯誤報告;您也可以等候螢幕畫面擷取完畢"</string>
diff --git a/packages/Shell/res/values-zu/strings.xml b/packages/Shell/res/values-zu/strings.xml
index a12a2b7..c1165ea 100644
--- a/packages/Shell/res/values-zu/strings.xml
+++ b/packages/Shell/res/values-zu/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_finished_title" msgid="4429132808670114081">"Umbiko wesiphazamisi ongu-<xliff:g id="ID">#%d</xliff:g> uthwetshuliwe"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Ingeza imininingwane kumbiko wesiphazamisi"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Sicela ulinde..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Swayiphela kwesokunxele ukuze wabelane umbiko wesiphazamiso sakho"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Umbiko wesiphazamisi uzobonakala efonini maduze"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Thepha ukuze wabelane ngombiko wakho wesiphazamisi"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Thepha ukuze wabelane ngombiko wesiphazamisi ngaphandle kwesithombe-skrini noma ulinde isithombe-skrini ukuthi siqede"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Thepha ukuze wabelane ngombiko wesiphazamisi ngaphandle kwesithombe-skrini noma ulinde isithombe-skrini ukuthi siqede"</string>
diff --git a/packages/Shell/res/values/strings.xml b/packages/Shell/res/values/strings.xml
index 3d6643d..5dfac95 100644
--- a/packages/Shell/res/values/strings.xml
+++ b/packages/Shell/res/values/strings.xml
@@ -25,8 +25,8 @@
<!-- Content notification indicating a bugreport is being updated before it can be shared, asking the user to wait [CHAR LIMIT=50] -->
<string name="bugreport_updating_wait">Please wait\u2026</string>
- <!-- Text of notification indicating that swipe left will share the captured bugreport. [CHAR LIMIT=100] -->
- <string name="bugreport_finished_text" product="watch">Swipe left to share your bug report</string>
+ <!-- Text of notification indicating that bugreport will appear on the phone. [CHAR LIMIT=100] -->
+ <string name="bugreport_finished_text" product="watch">The bug report will appear on the phone shortly</string>
<!-- Text of notification indicating that tapping will share the captured bugreport. [CHAR LIMIT=100] -->
<string name="bugreport_finished_text" product="default">Tap to share your bug report</string>
<!-- Text of notification indicating that swipe left will share the captured bugreport, but giving user the option to wait for the screenshot. [CHAR LIMIT=100] -->
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 6bc4df7..5a75f40 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -60,6 +60,8 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.hardware.display.DisplayManagerGlobal;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -78,6 +80,8 @@
import android.util.Log;
import android.util.Patterns;
import android.util.SparseArray;
+import android.view.Display;
+import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.View.OnFocusChangeListener;
@@ -212,6 +216,8 @@
private static final Bundle sNotificationBundle = new Bundle();
+ private boolean mIsWatch;
+
@Override
public void onCreate() {
mContext = getApplicationContext();
@@ -225,6 +231,9 @@
Log.w(TAG, "Could not create directory " + mScreenshotsDir);
}
}
+ final Configuration conf = mContext.getResources().getConfiguration();
+ mIsWatch = (conf.uiMode & Configuration.UI_MODE_TYPE_MASK) ==
+ Configuration.UI_MODE_TYPE_WATCH;
}
@Override
@@ -439,56 +448,68 @@
return;
}
- final NumberFormat nf = NumberFormat.getPercentInstance();
- nf.setMinimumFractionDigits(2);
- nf.setMaximumFractionDigits(2);
- final String percentageText = nf.format((double) info.progress / info.max);
- final Action cancelAction = new Action.Builder(null, mContext.getString(
- com.android.internal.R.string.cancel), newCancelIntent(mContext, info)).build();
- final Intent infoIntent = new Intent(mContext, BugreportProgressService.class);
- infoIntent.setAction(INTENT_BUGREPORT_INFO_LAUNCH);
- infoIntent.putExtra(EXTRA_ID, info.id);
- final PendingIntent infoPendingIntent =
- PendingIntent.getService(mContext, info.id, infoIntent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- final Action infoAction = new Action.Builder(null,
- mContext.getString(R.string.bugreport_info_action),
- infoPendingIntent).build();
- final Intent screenshotIntent = new Intent(mContext, BugreportProgressService.class);
- screenshotIntent.setAction(INTENT_BUGREPORT_SCREENSHOT);
- screenshotIntent.putExtra(EXTRA_ID, info.id);
- PendingIntent screenshotPendingIntent = mTakingScreenshot ? null : PendingIntent
- .getService(mContext, info.id, screenshotIntent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- final Action screenshotAction = new Action.Builder(null,
- mContext.getString(R.string.bugreport_screenshot_action),
- screenshotPendingIntent).build();
-
- final String title = mContext.getString(R.string.bugreport_in_progress_title, info.id);
-
- final String name =
- info.name != null ? info.name : mContext.getString(R.string.bugreport_unnamed);
-
- final Notification notification = newBaseNotification(mContext)
- .setContentTitle(title)
- .setTicker(title)
- .setContentText(name)
- .setProgress(info.max, info.progress, false)
- .setOngoing(true)
- .setContentIntent(infoPendingIntent)
- .setActions(infoAction, screenshotAction, cancelAction)
- .build();
-
if (info.finished) {
Log.w(TAG, "Not sending progress notification because bugreport has finished already ("
+ info + ")");
return;
}
+
+ final NumberFormat nf = NumberFormat.getPercentInstance();
+ nf.setMinimumFractionDigits(2);
+ nf.setMaximumFractionDigits(2);
+ final String percentageText = nf.format((double) info.progress / info.max);
+
+ String title = mContext.getString(R.string.bugreport_in_progress_title, info.id);
+
+ // TODO: Remove this workaround when notification progress is implemented on Wear.
+ if (mIsWatch) {
+ nf.setMinimumFractionDigits(0);
+ nf.setMaximumFractionDigits(0);
+ final String watchPercentageText = nf.format((double) info.progress / info.max);
+ title = title + "\n" + watchPercentageText;
+ }
+
+ final String name =
+ info.name != null ? info.name : mContext.getString(R.string.bugreport_unnamed);
+
+ final Notification.Builder builder = newBaseNotification(mContext)
+ .setContentTitle(title)
+ .setTicker(title)
+ .setContentText(name)
+ .setProgress(info.max, info.progress, false)
+ .setOngoing(true);
+
+ // Wear bugreport doesn't need the bug info dialog, screenshot and cancel action.
+ if (!mIsWatch) {
+ final Action cancelAction = new Action.Builder(null, mContext.getString(
+ com.android.internal.R.string.cancel), newCancelIntent(mContext, info)).build();
+ final Intent infoIntent = new Intent(mContext, BugreportProgressService.class);
+ infoIntent.setAction(INTENT_BUGREPORT_INFO_LAUNCH);
+ infoIntent.putExtra(EXTRA_ID, info.id);
+ final PendingIntent infoPendingIntent =
+ PendingIntent.getService(mContext, info.id, infoIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ final Action infoAction = new Action.Builder(null,
+ mContext.getString(R.string.bugreport_info_action),
+ infoPendingIntent).build();
+ final Intent screenshotIntent = new Intent(mContext, BugreportProgressService.class);
+ screenshotIntent.setAction(INTENT_BUGREPORT_SCREENSHOT);
+ screenshotIntent.putExtra(EXTRA_ID, info.id);
+ PendingIntent screenshotPendingIntent = mTakingScreenshot ? null : PendingIntent
+ .getService(mContext, info.id, screenshotIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ final Action screenshotAction = new Action.Builder(null,
+ mContext.getString(R.string.bugreport_screenshot_action),
+ screenshotPendingIntent).build();
+ builder.setContentIntent(infoPendingIntent)
+ .setActions(infoAction, screenshotAction, cancelAction);
+ }
+
if (DEBUG) {
Log.d(TAG, "Sending 'Progress' notification for id " + info.id + " (pid " + info.pid
+ "): " + percentageText);
}
- sendForegroundabledNotification(info.id, notification);
+ sendForegroundabledNotification(info.id, builder.build());
}
private void sendForegroundabledNotification(int id, Notification notification) {
@@ -754,7 +775,6 @@
}
msg = mContext.getString(R.string.bugreport_screenshot_taken);
} else {
- // TODO: try again using Framework APIs instead of relying on screencap.
msg = mContext.getString(R.string.bugreport_screenshot_failed);
Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
}
@@ -854,10 +874,7 @@
// Stop running on foreground, otherwise share notification cannot be dismissed.
stopForegroundWhenDone(id);
- final Configuration conf = mContext.getResources().getConfiguration();
- if ((conf.uiMode & Configuration.UI_MODE_TYPE_MASK) != Configuration.UI_MODE_TYPE_WATCH) {
- triggerLocalNotification(mContext, info);
- }
+ triggerLocalNotification(mContext, info);
}
/**
@@ -1336,22 +1353,24 @@
/**
* Takes a screenshot and save it to the given location.
*/
- private static boolean takeScreenshot(Context context, String screenshotFile) {
- final ProcessBuilder screencap = new ProcessBuilder()
- .command("/system/bin/screencap", "-p", screenshotFile);
- Log.d(TAG, "Taking screenshot using " + screencap.command());
- try {
- final int exitValue = screencap.start().waitFor();
- if (exitValue == 0) {
+ private static boolean takeScreenshot(Context context, String path) {
+ final Bitmap bitmap = Screenshooter.takeScreenshot();
+ if (bitmap == null) {
+ return false;
+ }
+ boolean status;
+ try (final FileOutputStream fos = new FileOutputStream(path)) {
+ if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)) {
((Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE)).vibrate(150);
return true;
+ } else {
+ Log.e(TAG, "Failed to save screenshot on " + path);
}
- Log.e(TAG, "screencap (" + screencap.command() + ") failed: " + exitValue);
- } catch (IOException e) {
- Log.e(TAG, "screencap (" + screencap.command() + ") failed", e);
- } catch (InterruptedException e) {
- Log.w(TAG, "Thread interrupted while screencap still running");
- Thread.currentThread().interrupt();
+ } catch (IOException e ) {
+ Log.e(TAG, "Failed to save screenshot on " + path, e);
+ return false;
+ } finally {
+ bitmap.recycle();
}
return false;
}
diff --git a/packages/Shell/src/com/android/shell/Screenshooter.java b/packages/Shell/src/com/android/shell/Screenshooter.java
new file mode 100644
index 0000000..ce0fbbc
--- /dev/null
+++ b/packages/Shell/src/com/android/shell/Screenshooter.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.shell;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.hardware.display.DisplayManagerGlobal;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Display;
+import android.view.Surface;
+import android.view.SurfaceControl;
+
+/**
+ * Helper class used to take screenshots.
+ *
+ * TODO: logic below was copied and pasted from UiAutomation; it should be refactored into a common
+ * component that could be used by both (Shell and UiAutomation).
+ */
+final class Screenshooter {
+
+ private static final String TAG = "Screenshooter";
+
+ /** Rotation constant: Freeze rotation to 0 degrees (natural orientation) */
+ public static final int ROTATION_FREEZE_0 = Surface.ROTATION_0;
+
+ /** Rotation constant: Freeze rotation to 90 degrees . */
+ public static final int ROTATION_FREEZE_90 = Surface.ROTATION_90;
+
+ /** Rotation constant: Freeze rotation to 180 degrees . */
+ public static final int ROTATION_FREEZE_180 = Surface.ROTATION_180;
+
+ /** Rotation constant: Freeze rotation to 270 degrees . */
+ public static final int ROTATION_FREEZE_270 = Surface.ROTATION_270;
+
+ /**
+ * Takes a screenshot.
+ *
+ * @return The screenshot bitmap on success, null otherwise.
+ */
+ static Bitmap takeScreenshot() {
+ Display display = DisplayManagerGlobal.getInstance()
+ .getRealDisplay(Display.DEFAULT_DISPLAY);
+ Point displaySize = new Point();
+ display.getRealSize(displaySize);
+ final int displayWidth = displaySize.x;
+ final int displayHeight = displaySize.y;
+
+ final float screenshotWidth;
+ final float screenshotHeight;
+
+ final int rotation = display.getRotation();
+ switch (rotation) {
+ case ROTATION_FREEZE_0: {
+ screenshotWidth = displayWidth;
+ screenshotHeight = displayHeight;
+ } break;
+ case ROTATION_FREEZE_90: {
+ screenshotWidth = displayHeight;
+ screenshotHeight = displayWidth;
+ } break;
+ case ROTATION_FREEZE_180: {
+ screenshotWidth = displayWidth;
+ screenshotHeight = displayHeight;
+ } break;
+ case ROTATION_FREEZE_270: {
+ screenshotWidth = displayHeight;
+ screenshotHeight = displayWidth;
+ } break;
+ default: {
+ throw new IllegalArgumentException("Invalid rotation: "
+ + rotation);
+ }
+ }
+
+ Log.d(TAG, "Taking screenshot of dimensions " + displayWidth + " x " + displayHeight);
+ // Take the screenshot
+ Bitmap screenShot =
+ SurfaceControl.screenshot((int) screenshotWidth, (int) screenshotHeight);
+ if (screenShot == null) {
+ Log.e(TAG, "Failed to take screenshot of dimensions " + screenshotWidth + " x "
+ + screenshotHeight);
+ return null;
+ }
+
+ // Rotate the screenshot to the current orientation
+ if (rotation != ROTATION_FREEZE_0) {
+ Bitmap unrotatedScreenShot = Bitmap.createBitmap(displayWidth, displayHeight,
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(unrotatedScreenShot);
+ canvas.translate(unrotatedScreenShot.getWidth() / 2,
+ unrotatedScreenShot.getHeight() / 2);
+ canvas.rotate(getDegreesForRotation(rotation));
+ canvas.translate(- screenshotWidth / 2, - screenshotHeight / 2);
+ canvas.drawBitmap(screenShot, 0, 0, null);
+ canvas.setBitmap(null);
+ screenShot.recycle();
+ screenShot = unrotatedScreenShot;
+ }
+
+ // Optimization
+ screenShot.setHasAlpha(false);
+
+ return screenShot;
+ }
+
+ private static float getDegreesForRotation(int value) {
+ switch (value) {
+ case Surface.ROTATION_90: {
+ return 360f - 90f;
+ }
+ case Surface.ROTATION_180: {
+ return 360f - 180f;
+ }
+ case Surface.ROTATION_270: {
+ return 360f - 270f;
+ } default: {
+ return 0;
+ }
+ }
+ }
+
+}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index eadb4bf..bdb103a 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -165,6 +165,9 @@
<!-- the ability to rename notifications posted by other apps -->
<uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
+ <!-- shortcut manager -->
+ <uses-permission android:name="android.permission.RESET_SHORTCUT_MANAGER_THROTTLING" />
+
<application
android:name=".SystemUIApplication"
android:persistent="true"
@@ -385,7 +388,7 @@
android:theme="@style/PipTheme"
android:launchMode="singleTop"
android:taskAffinity=""
- android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|locale|layoutDirection"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
androidprv:alwaysFocusable="true"
@@ -395,7 +398,7 @@
android:exported="true"
android:theme="@style/PipTheme"
android:taskAffinity=""
- android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|locale|layoutDirection"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
android:excludeFromRecents="true" />
diff --git a/packages/SystemUI/res/color/notification_guts_buttons.xml b/packages/SystemUI/res/color/notification_guts_buttons.xml
index c211e43..3b8d59b 100644
--- a/packages/SystemUI/res/color/notification_guts_buttons.xml
+++ b/packages/SystemUI/res/color/notification_guts_buttons.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true"
- android:color="@*android:color/material_deep_teal_500" />
+ android:color="?android:attr/colorAccent" />
<item android:color="@android:color/black"
android:alpha=".54" />
</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/qs_detail_empty.xml b/packages/SystemUI/res/color/qs_detail_empty.xml
new file mode 100644
index 0000000..4be39c7
--- /dev/null
+++ b/packages/SystemUI/res/color/qs_detail_empty.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:alpha="0.14" android:color="@*android:color/quaternary_device_default_settings" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/night_mode_settings.xml b/packages/SystemUI/res/color/qs_detail_progress_track.xml
similarity index 67%
copy from packages/SystemUI/res/layout/night_mode_settings.xml
copy to packages/SystemUI/res/color/qs_detail_progress_track.xml
index 3725e78..c56382e 100644
--- a/packages/SystemUI/res/layout/night_mode_settings.xml
+++ b/packages/SystemUI/res/color/qs_detail_progress_track.xml
@@ -13,12 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <include layout="@layout/switch_bar" />
-
-</LinearLayout>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- I really don't want to define this, but the View that uses this asset uses both the
+ light and dark accent colors. -->
+ <item android:alpha="0.6" android:drawable="@*android:color/accent_device_default_light" />
+</selector>
diff --git a/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml b/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml
index 20251c2..344859c 100644
--- a/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml
+++ b/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml
@@ -17,6 +17,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_activated="true" android:color="@color/current_user_border_color" />
+ <item android:state_activated="true" android:color="?android:attr/colorAccent" />
<item android:color="@android:color/transparent" />
</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/qs_user_detail_name.xml b/packages/SystemUI/res/color/qs_user_detail_name.xml
index 35c7a4f..e262209 100644
--- a/packages/SystemUI/res/color/qs_user_detail_name.xml
+++ b/packages/SystemUI/res/color/qs_user_detail_name.xml
@@ -17,7 +17,7 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_activated="true" android:color="@color/current_user_border_color" />
+ <item android:state_activated="true" android:color="?android:attr/colorAccent" />
<item android:state_enabled="false" android:color="@color/qs_tile_disabled_color" />
<item android:color="#66ffffff" /> <!-- 40% white -->
</selector>
\ No newline at end of file
diff --git a/packages/DocumentsUI/res/values/attrs.xml b/packages/SystemUI/res/color/screen_pinning_nav_icon_highlight_outer.xml
similarity index 73%
copy from packages/DocumentsUI/res/values/attrs.xml
copy to packages/SystemUI/res/color/screen_pinning_nav_icon_highlight_outer.xml
index 9e13001..5370f7a 100644
--- a/packages/DocumentsUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/color/screen_pinning_nav_icon_highlight_outer.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- 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.
@@ -13,8 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources>
- <declare-styleable name="DocumentsTheme">
- <attr name="colorActionMode" format="color"/>
- </declare-styleable>
-</resources>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:alpha="0.25" android:color="?android:attr/colorAccent" />
+</selector>
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_carmode.png
index 6242084..95e5778 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_carmode.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_carmode.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_carmode.png
index 1b37a47..6421146 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_carmode.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_carmode.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_carmode.png
index 9e05758..151d5fe 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_carmode.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_carmode.png
index 2fcfdde..b954aa7 100644
--- a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_carmode.png
+++ b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_carmode.png
index 48708a5..61d5db6 100644
--- a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_carmode.png
+++ b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_carmode.png
index 3d73184..7b98c1f 100644
--- a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_carmode.png
+++ b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_carmode.png
index 786935d..aad1320 100644
--- a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_carmode.png
+++ b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_carmode.png
index e4bd4bc..754b2d9 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_carmode.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_carmode.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_carmode.png
index 94ccf79..873ed7f 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_carmode.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_carmode.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_carmode.png
index 980bbbc..7696d87 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_carmode.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_carmode.png
index 201be3b..c98f55e 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_carmode.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_carmode.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_carmode.png
index 2770d62..187a566 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_carmode.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_carmode.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_carmode.png
index 8ac6493..c66f8be 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_carmode.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_carmode.png
index 8e3678b..3a3a119 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_carmode.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_carmode.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_carmode.png
index 6c7cb05..7198c82 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_carmode.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_carmode.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_carmode.png
index ea2b108..b1fc02e 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_carmode.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_carmode.png
index 3e11023..c06bfda 100644
--- a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_carmode.png
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_carmode.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_carmode.png
index fe8213d..a8c76bf 100644
--- a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_carmode.png
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_carmode.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_carmode.png
index c117efd..b798e3d 100644
--- a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_carmode.png
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/brightness_mirror_background.xml b/packages/SystemUI/res/drawable/brightness_mirror_background.xml
index e901e40..0c69d89 100644
--- a/packages/SystemUI/res/drawable/brightness_mirror_background.xml
+++ b/packages/SystemUI/res/drawable/brightness_mirror_background.xml
@@ -15,5 +15,5 @@
~ limitations under the License
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="@color/system_primary_color" />
+ <solid android:color="?android:attr/colorPrimary" />
</shape>
diff --git a/packages/SystemUI/res/drawable/fab_background.xml b/packages/SystemUI/res/drawable/fab_background.xml
deleted file mode 100644
index 7f23f2b..0000000
--- a/packages/SystemUI/res/drawable/fab_background.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 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.
--->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/fab_ripple">
- <item>
- <shape>
- <solid android:color="@color/fab_shape" />
- </shape>
- </item>
-</ripple>
diff --git a/packages/SystemUI/res/drawable/ic_brightness_thumb.xml b/packages/SystemUI/res/drawable/ic_brightness_thumb.xml
index dc978fe..24ac018 100644
--- a/packages/SystemUI/res/drawable/ic_brightness_thumb.xml
+++ b/packages/SystemUI/res/drawable/ic_brightness_thumb.xml
@@ -20,7 +20,7 @@
android:viewportHeight="24.0">
<path
android:pathData="m18.250000,12.000000a6.250000,6.250000 0.000000,1.000000 1.000000,-12.500000 0.000000,6.250000 6.250000,0.000000 1.000000,1.000000 12.500000,0.000000z"
- android:fillColor="@color/system_primary_color" />
+ android:fillColor="?android:attr/colorPrimary" />
<path
android:fillColor="#FFFFFFFF"
android:pathData="M20.000000,8.700000L20.000000,4.000000L15.300000,4.000000L12.000000,0.700000 8.700000,4.000000L4.000000,4.000000L4.000000,8.700000L0.700000,12.000000 4.000000,15.300000L4.000000,20.000000L8.700000,20.000000L12.000000,23.299999 15.300000,20.000000L20.000000,20.000000L20.000000,15.300000L23.299999,12.000000 20.000000,8.700000zM12.000000,18.000000C8.700000,18.000000 6.000000,15.300000 6.000000,12.000000 6.000000,8.700000 8.700000,6.000000 12.000000,6.000000c3.300000,0.000000 6.000000,2.700000 6.000000,6.000000 0.000000,3.300000 -2.700000,6.000000 -6.000000,6.000000zM12.000000,8.000000c-2.200000,0.000000 -4.000000,1.800000 -4.000000,4.000000 0.000000,2.200000 1.800000,4.000000 4.000000,4.000000 2.200000,0.000000 4.000000,-1.800000 4.000000,-4.000000 0.000000,-2.200000 -1.800000,-4.000000 -4.000000,-4.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_night_mode_disabled.xml b/packages/SystemUI/res/drawable/ic_mode_edit.xml
similarity index 66%
rename from packages/SystemUI/res/drawable/ic_night_mode_disabled.xml
rename to packages/SystemUI/res/drawable/ic_mode_edit.xml
index 010815a..8a73686 100644
--- a/packages/SystemUI/res/drawable/ic_night_mode_disabled.xml
+++ b/packages/SystemUI/res/drawable/ic_mode_edit.xml
@@ -14,11 +14,11 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
- android:height="24.0dp"
+ android:width="20.0dp"
+ android:height="20.0dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
- android:fillColor="#4DFFFFFF"
- android:pathData="M20.71,5.63l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0.0l-3.12,3.12 -1.93,-1.91 -1.41,1.41 1.42,1.42L3.0,16.25L3.0,21.0l4.75,0.0l8.92,-8.92 1.42,1.42 1.41,-1.41 -1.92,-1.92 3.12,-3.12c0.4,0.0 0.4,-1.0 0.01,-1.42zM6.92,19.0L5.0,17.08l8.06,-8.06 1.92,1.92L6.92,19.0z"/>
+ android:fillColor="#FFFFFF"
+ android:pathData="M3.0,17.25L3.0,21.0l3.75,0.0L17.81,9.94l-3.75,-3.75L3.0,17.25zM20.71,7.04c0.39,-0.3 0.39,-1.02 0.0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0.0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_night_mode.xml b/packages/SystemUI/res/drawable/ic_night_mode.xml
deleted file mode 100644
index caa7a47..0000000
--- a/packages/SystemUI/res/drawable/ic_night_mode.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
- android:height="24.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M20.71,5.63l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0.0l-3.12,3.12 -1.93,-1.91 -1.41,1.41 1.42,1.42L3.0,16.25L3.0,21.0l4.75,0.0l8.92,-8.92 1.42,1.42 1.41,-1.41 -1.92,-1.92 3.12,-3.12c0.4,0.0 0.4,-1.0 0.01,-1.42zM6.92,19.0L5.0,17.08l8.06,-8.06 1.92,1.92L6.92,19.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_branded_vpn.xml b/packages/SystemUI/res/drawable/ic_qs_branded_vpn.xml
new file mode 100644
index 0000000..736a04a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_branded_vpn.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="12.0dp"
+ android:height="12.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#4DFFFFFF"
+ android:pathData="M12.700000,10.000000c-0.800000,-2.300000 -3.000000,-4.000000 -5.700000,-4.000000c-3.300000,0.000000 -6.000000,2.700000 -6.000000,6.000000s2.700000,6.000000 6.000000,6.000000c2.600000,0.000000 4.800000,-1.700000 5.700000,-4.000000L17.000000,14.000000l0.000000,4.000000l4.000000,0.000000l0.000000,-4.000000l2.000000,0.000000l0.000000,-4.000000L12.700000,10.000000zM7.000000,14.000000c-1.100000,0.000000 -2.000000,-0.900000 -2.000000,-2.000000c0.000000,-1.100000 0.900000,-2.000000 2.000000,-2.000000s2.000000,0.900000 2.000000,2.000000C9.000000,13.100000 8.100000,14.000000 7.000000,14.000000z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_night_mode_disabled.xml b/packages/SystemUI/res/drawable/ic_qs_night_display_off.xml
similarity index 60%
copy from packages/SystemUI/res/drawable/ic_night_mode_disabled.xml
copy to packages/SystemUI/res/drawable/ic_qs_night_display_off.xml
index 010815a..778ccbc 100644
--- a/packages/SystemUI/res/drawable/ic_night_mode_disabled.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_night_display_off.xml
@@ -14,11 +14,14 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
- android:height="24.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
+ android:width="64dp"
+ android:height="64dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:alpha="0.3">
+
<path
- android:fillColor="#4DFFFFFF"
- android:pathData="M20.71,5.63l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0.0l-3.12,3.12 -1.93,-1.91 -1.41,1.41 1.42,1.42L3.0,16.25L3.0,21.0l4.75,0.0l8.92,-8.92 1.42,1.42 1.41,-1.41 -1.92,-1.92 3.12,-3.12c0.4,0.0 0.4,-1.0 0.01,-1.42zM6.92,19.0L5.0,17.08l8.06,-8.06 1.92,1.92L6.92,19.0z"/>
+ android:fillColor="#FFF"
+ android:pathData="M6,12c0,5.5,4.5,10,10,10c1,0,2-0.2,3-0.5c-4.1-1.3-7-5.1-7-9.5s2.9-8.3,7-9.5C18.1,2.2,17.1,2,16,2C10.5,2,6,6.5,6,12z" />
+
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_night_mode_disabled.xml b/packages/SystemUI/res/drawable/ic_qs_night_display_on.xml
similarity index 60%
copy from packages/SystemUI/res/drawable/ic_night_mode_disabled.xml
copy to packages/SystemUI/res/drawable/ic_qs_night_display_on.xml
index 010815a..aaca663 100644
--- a/packages/SystemUI/res/drawable/ic_night_mode_disabled.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_night_display_on.xml
@@ -14,11 +14,13 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
- android:height="24.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
+ android:width="64dp"
+ android:height="64dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
<path
- android:fillColor="#4DFFFFFF"
- android:pathData="M20.71,5.63l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0.0l-3.12,3.12 -1.93,-1.91 -1.41,1.41 1.42,1.42L3.0,16.25L3.0,21.0l4.75,0.0l8.92,-8.92 1.42,1.42 1.41,-1.41 -1.92,-1.92 3.12,-3.12c0.4,0.0 0.4,-1.0 0.01,-1.42zM6.92,19.0L5.0,17.08l8.06,-8.06 1.92,1.92L6.92,19.0z"/>
+ android:fillColor="#FFF"
+ android:pathData="M6,12c0,5.5,4.5,10,10,10c1,0,2-0.2,3-0.5c-4.1-1.3-7-5.1-7-9.5s2.9-8.3,7-9.5C18.1,2.2,17.1,2,16,2C10.5,2,6,6.5,6,12z" />
+
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml b/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml
new file mode 100644
index 0000000..4d7f325
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml
@@ -0,0 +1,30 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M4.6,7.8l0.7,0.0l0.0,1.3L4.6,9.1L4.6,11.0L3.0,11.0L3.0,9.2L0.1,9.2L0.0,8.2l3.0,-5.7l1.7,0.0L4.6,7.8L4.6,7.8zM1.7,7.8L3.0,7.8l0.0,-3.0L2.9,5.0L1.7,7.8z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M11.9,9.9c-0.2,0.4 -0.6,0.7 -1.0,0.9s-1.0,0.4 -1.8,0.4c-0.9,0.0 -1.7,-0.3 -2.2,-0.8S6.1,9.0 6.1,7.9L6.1,5.6c0.0,-1.1 0.3,-1.9 0.8,-2.4S8.2,2.4 9.0,2.4c1.0,0.0 1.7,0.2 2.1,0.7s0.7,1.2 0.7,2.1l-1.6,0.0c0.0,-0.5 -0.1,-0.9 -0.2,-1.1S9.5,3.7 9.0,3.7c-0.4,0.0 -0.7,0.2 -0.9,0.5S7.8,5.0 7.8,5.6l0.0,2.3c0.0,0.7 0.1,1.1 0.3,1.4c0.2,0.3 0.6,0.5 1.0,0.5c0.3,0.0 0.6,0.0 0.7,-0.1s0.3,-0.2 0.4,-0.3L10.2,7.8L9.0,7.8L9.0,6.6l2.9,0.0L11.9,9.9z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M17.7,4.4l-1.900001,0.0 0.0,-1.9 -1.2,0.0 0.0,1.9 -1.900001,0.0 0.0,1.2 1.900001,0.0 0.0,1.9 1.2,0.0 0.0,-1.9 1.900001,0.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_lte_plus.xml b/packages/SystemUI/res/drawable/ic_qs_signal_lte_plus.xml
new file mode 100644
index 0000000..3af0629
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_lte_plus.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M2.0,9.7l2.0,0.0L4.0,11.0L0.4,11.0L0.4,2.5L2.0,2.5L2.0,9.7z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M8.3,3.8L7.0,3.8L7.0,11.0L5.4,11.0L5.4,3.8L4.0,3.8L4.0,2.5l4.3,0.0L8.3,3.8z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12.4,7.3l-1.7,0.0l0.0,2.4l2.1,0.0L12.799999,11.0L9.0,11.0L9.0,2.5l3.7,0.0l0.0,1.3l-2.1,0.0l0.0,2.1l1.7,0.0L12.4,7.3L12.4,7.3z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M18.4,4.4l-1.9,0.0 0.0,-1.9 -1.2,0.0 0.0,1.9 -1.900001,0.0 0.0,1.2 1.900001,0.0 0.0,1.9 1.2,0.0 0.0,-1.9 1.9,0.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_settings_20dp.xml b/packages/SystemUI/res/drawable/ic_settings_20dp.xml
new file mode 100644
index 0000000..3170f86
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_settings_20dp.xml
@@ -0,0 +1,24 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M19.4,13.0c0.0,-0.3 0.1,-0.6 0.1,-1.0s0.0,-0.7 -0.1,-1.0l2.1,-1.7c0.2,-0.2 0.2,-0.4 0.1,-0.6l-2.0,-3.5C19.5,5.1 19.3,5.0 19.0,5.1l-2.5,1.0c-0.5,-0.4 -1.1,-0.7 -1.7,-1.0l-0.4,-2.6C14.5,2.2 14.2,2.0 14.0,2.0l-4.0,0.0C9.8,2.0 9.5,2.2 9.5,2.4L9.1,5.1C8.5,5.3 8.0,5.7 7.4,6.1L5.0,5.1C4.7,5.0 4.5,5.1 4.3,5.3l-2.0,3.5C2.2,8.9 2.3,9.2 2.5,9.4L4.6,11.0c0.0,0.3 -0.1,0.6 -0.1,1.0s0.0,0.7 0.1,1.0l-2.1,1.7c-0.2,0.2 -0.2,0.4 -0.1,0.6l2.0,3.5C4.5,18.9 4.7,19.0 5.0,18.9l2.5,-1.0c0.5,0.4 1.1,0.7 1.7,1.0l0.4,2.6c0.0,0.2 0.2,0.4 0.5,0.4l4.0,0.0c0.2,0.0 0.5,-0.2 0.5,-0.4l0.4,-2.6c0.6,-0.3 1.2,-0.6 1.7,-1.0l2.5,1.0c0.2,0.1 0.5,0.0 0.6,-0.2l2.0,-3.5c0.1,-0.2 0.1,-0.5 -0.1,-0.6L19.4,13.0zM12.0,15.5c-1.9,0.0 -3.5,-1.6 -3.5,-3.5s1.6,-3.5 3.5,-3.5s3.5,1.6 3.5,3.5S13.9,15.5 12.0,15.5z"
+ android:fillColor="#ffffffff" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/lockscreen_fingerprint_error_state_to_fp.xml b/packages/SystemUI/res/drawable/lockscreen_fingerprint_error_state_to_fp.xml
index 67e6a01..e207cb3 100644
--- a/packages/SystemUI/res/drawable/lockscreen_fingerprint_error_state_to_fp.xml
+++ b/packages/SystemUI/res/drawable/lockscreen_fingerprint_error_state_to_fp.xml
@@ -98,11 +98,11 @@
<path
android:name="path_2"
android:pathData="M 1.35900878906,6.76104736328 c 0.0,0.0 -2.69998168945,0.0 -2.69998168945,0.0 c 0.0,0.0 0.0,-2.69995117188 0.0,-2.69995117188 c 0.0,0.0 2.69998168945,0.0 2.69998168945,0.0 c 0.0,0.0 0.0,2.69995117188 0.0,2.69995117188 Z"
- android:fillColor="#FFF2501D" />
+ android:fillColor="@*android:color/system_error" />
<path
android:name="path_1"
android:pathData="M 1.35363769531,1.36633300781 c 0.0,0.0 -2.69998168945,0.0 -2.69998168945,0.0 c 0.0,0.0 0.0,-8.09997558594 0.0,-8.09997558594 c 0.0,0.0 2.69998168945,0.0 2.69998168945,0.0 c 0.0,0.0 0.0,8.09997558594 0.0,8.09997558594 Z"
- android:fillColor="#FFF2501D" />
+ android:fillColor="@*android:color/system_error" />
</group>
</group>
<group
@@ -117,7 +117,7 @@
<path
android:name="path_3"
android:pathData="M 0.0101470947266,10.8087768555 c -5.96701049805,0.0 -10.8000183105,-4.8330078125 -10.8000183105,-10.8000488281 c 0.0,-5.96691894531 4.8330078125,-10.7999267578 10.8000183105,-10.7999267578 c 5.96697998047,0.0 10.799987793,4.8330078125 10.799987793,10.7999267578 c 0.0,5.96704101562 -4.8330078125,10.8000488281 -10.799987793,10.8000488281 Z"
- android:strokeColor="#FFF2501D"
+ android:strokeColor="@*android:color/system_error"
android:strokeWidth="2"
android:trimPathStart="0"
android:trimPathEnd="1" />
diff --git a/packages/SystemUI/res/drawable/lockscreen_fingerprint_fp_to_error_state.xml b/packages/SystemUI/res/drawable/lockscreen_fingerprint_fp_to_error_state.xml
index bbadec1..2b4babc 100644
--- a/packages/SystemUI/res/drawable/lockscreen_fingerprint_fp_to_error_state.xml
+++ b/packages/SystemUI/res/drawable/lockscreen_fingerprint_fp_to_error_state.xml
@@ -96,7 +96,7 @@
<path
android:name="ridge_5_path_0"
android:pathData="M -25.3591003418,-24.4138946533 c -0.569000244141,0.106399536133 -1.12660217285,0.140594482422 -1.45460510254,0.140594482422 c -1.29689025879,0.0 -2.53239440918,-0.343307495117 -3.62019348145,-1.12400817871 c -1.67700195312,-1.20349121094 -2.76950073242,-3.17008972168 -2.76950073242,-5.39189147949"
- android:strokeColor="#FFF2501D"
+ android:strokeColor="@*android:color/system_error"
android:strokeWidth="1.45"
android:strokeLineCap="round"
android:trimPathEnd="0" />
@@ -106,7 +106,7 @@
<path
android:name="ridge_7_path_0"
android:pathData="M -36.1409912109,-21.7843475342 c -1.00540161133,-1.19300842285 -1.57499694824,-1.9181060791 -2.36520385742,-3.50170898438 c -0.827560424805,-1.65869140625 -1.31352233887,-3.49159240723 -1.31352233887,-5.48489379883 c 0.0,-3.66279602051 2.96932983398,-6.63220214844 6.63221740723,-6.63220214844 c 3.6628112793,0.0 6.63220214844,2.96940612793 6.63220214844,6.63220214844"
- android:strokeColor="#FFF2501D"
+ android:strokeColor="@*android:color/system_error"
android:strokeWidth="1.45"
android:strokeLineCap="round"
android:trimPathEnd="0" />
@@ -116,7 +116,7 @@
<path
android:name="ridge_6_path_0"
android:pathData="M -42.1907958984,-25.6756896973 c -0.758117675781,-2.14370727539 -0.896545410156,-3.86891174316 -0.896545410156,-5.12921142578 c 0.0,-1.46069335938 0.249176025391,-2.84799194336 0.814682006836,-4.09748840332 c 1.56153869629,-3.45030212402 5.03434753418,-5.85076904297 9.0679473877,-5.85076904297 c 5.49430847168,0.0 9.94830322266,4.4539642334 9.94830322266,9.94825744629 c 0.0,1.83151245117 -1.48460388184,3.31610107422 -3.31610107422,3.31610107422 c -1.83149719238,0.0 -3.31610107422,-1.48469543457 -3.31610107422,-3.31610107422 c 0.0,-1.83139038086 -1.48458862305,-3.31610107422 -3.31610107422,-3.31610107422 c -1.83149719238,0.0 -3.31610107422,1.48471069336 -3.31610107422,3.31610107422 c 0.0,2.57020568848 0.989517211914,4.88710021973 2.60510253906,6.5865020752 c 1.22210693359,1.28550720215 2.43139648438,2.09950256348 4.47590637207,2.69030761719"
- android:strokeColor="#FFF2501D"
+ android:strokeColor="@*android:color/system_error"
android:strokeWidth="1.45"
android:strokeLineCap="round"
android:trimPathEnd="0" />
@@ -126,7 +126,7 @@
<path
android:name="ridge_2_path_0"
android:pathData="M -44.0646514893,-38.1672973633 c 1.19026184082,-1.77430725098 2.67503356934,-3.24531555176 4.55902099609,-4.27278137207 c 1.88395690918,-1.0274810791 4.04466247559,-1.61137390137 6.34175109863,-1.61137390137 c 2.28761291504,0.0 4.43991088867,0.579071044922 6.31831359863,1.59861755371 c 1.8784942627,1.01954650879 3.36059570312,2.4796295166 4.55279541016,4.24153137207"
- android:strokeColor="#FFF2501D"
+ android:strokeColor="@*android:color/system_error"
android:strokeWidth="1.45"
android:strokeLineCap="round"
android:trimPathStart="1" />
@@ -138,7 +138,7 @@
<path
android:name="ridge_1_path_0"
android:pathData="M 71.7812347412,97.0507202148 c -2.27149963379,-1.31344604492 -4.71360778809,-2.07006835938 -7.56221008301,-2.07006835938 c -2.84869384766,0.0 -5.23320007324,0.779556274414 -7.34411621094,2.07006835938"
- android:strokeColor="#FFF2501D"
+ android:strokeColor="@*android:color/system_error"
android:strokeWidth="1.45"
android:strokeLineCap="round"
android:trimPathEnd="0" />
@@ -157,11 +157,11 @@
<path
android:name="path_2"
android:pathData="M -1.3705291748,4.06730651855 c 0.0,0.0 0.036376953125,-0.0102386474609 0.036376953125,-0.0102386474609 c 0.0,0.0 -0.00682067871094,0.0040283203125 -0.00682067871093,0.0040283203125 c 0.0,0.0 -0.0161437988281,0.00479125976562 -0.0161437988281,0.00479125976563 c 0.0,0.0 -0.0134124755859,0.00141906738281 -0.0134124755859,0.00141906738281 Z"
- android:fillColor="#FFF2501D" />
+ android:fillColor="@*android:color/system_error" />
<path
android:name="path_1"
android:pathData="M -1.3737487793,-6.77532958984 c 0.0,0.0 0.00604248046875,0.0166625976562 0.00604248046876,0.0166625976562 c 0.0,0.0 0.0213623046875,0.0250244140625 0.0213623046875,0.0250244140625 c 0.0,0.0 -0.00604248046875,-0.0166625976562 -0.00604248046876,-0.0166625976562 c 0.0,0.0 -0.0213623046875,-0.0250244140625 -0.0213623046875,-0.0250244140625 Z"
- android:fillColor="#FFF2501D" />
+ android:fillColor="@*android:color/system_error" />
</group>
</group>
<group
@@ -176,7 +176,7 @@
<path
android:name="path_3"
android:pathData="M 0.0101470947266,10.8087768555 c -5.96701049805,0.0 -10.8000183105,-4.8330078125 -10.8000183105,-10.8000488281 c 0.0,-5.96691894531 4.8330078125,-10.7999267578 10.8000183105,-10.7999267578 c 5.96697998047,0.0 10.799987793,4.8330078125 10.799987793,10.7999267578 c 0.0,5.96704101562 -4.8330078125,10.8000488281 -10.799987793,10.8000488281 Z"
- android:strokeColor="#FFF2501D"
+ android:strokeColor="@*android:color/system_error"
android:strokeWidth="2"
android:trimPathStart="1" />
</group>
diff --git a/packages/SystemUI/res/drawable/qs_background_primary.xml b/packages/SystemUI/res/drawable/qs_background_primary.xml
index 1bf7d4c..8ea9e06 100644
--- a/packages/SystemUI/res/drawable/qs_background_primary.xml
+++ b/packages/SystemUI/res/drawable/qs_background_primary.xml
@@ -15,6 +15,6 @@
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape>
- <solid android:color="@color/system_primary_color"/>
+ <solid android:color="?android:attr/colorPrimary"/>
</shape>
</inset>
diff --git a/packages/SystemUI/res/drawable/qs_customizer_background.xml b/packages/SystemUI/res/drawable/qs_customizer_background.xml
index d90f820..12d8016 100644
--- a/packages/SystemUI/res/drawable/qs_customizer_background.xml
+++ b/packages/SystemUI/res/drawable/qs_customizer_background.xml
@@ -15,5 +15,5 @@
-->
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/qs_detail_transition" />
- <item android:drawable="@color/system_primary_color" />
+ <item android:drawable="?android:attr/colorPrimary" />
</transition>
diff --git a/packages/SystemUI/res/drawable/qs_detail_background.xml b/packages/SystemUI/res/drawable/qs_detail_background.xml
index 692cd44..84c793f 100644
--- a/packages/SystemUI/res/drawable/qs_detail_background.xml
+++ b/packages/SystemUI/res/drawable/qs_detail_background.xml
@@ -15,5 +15,5 @@
-->
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/qs_detail_transition" />
- <item android:drawable="@color/system_primary_color" />
+ <item android:drawable="?android:attr/colorPrimary" />
</transition>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/quick_header_bg.xml b/packages/SystemUI/res/drawable/quick_header_bg.xml
index d45d673..920e6f5 100644
--- a/packages/SystemUI/res/drawable/quick_header_bg.xml
+++ b/packages/SystemUI/res/drawable/quick_header_bg.xml
@@ -17,5 +17,5 @@
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:attr/colorControlHighlight" >
- <item android:drawable="@color/system_primary_color"/>
+ <item android:drawable="?android:attr/colorPrimary"/>
</ripple>
diff --git a/packages/SystemUI/res/drawable/recents_stack_action_background.xml b/packages/SystemUI/res/drawable/recents_stack_action_background.xml
new file mode 100644
index 0000000..2a40dd0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_stack_action_background.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2 (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.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape>
+ <corners android:radius="@dimen/recents_task_view_rounded_corners_radius" />
+ <solid android:color="@android:color/white" />
+ </shape>
+ </item>
+</ripple>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/screen_pinning_bg_circ.xml b/packages/SystemUI/res/drawable/screen_pinning_bg_circ.xml
index 354b8bd..1303bdb 100644
--- a/packages/SystemUI/res/drawable/screen_pinning_bg_circ.xml
+++ b/packages/SystemUI/res/drawable/screen_pinning_bg_circ.xml
@@ -19,7 +19,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" >
- <solid android:color="@color/system_accent_color" />
+ <solid android:color="?android:attr/colorAccent" />
<size
android:height="@dimen/screen_pinning_nav_highlight_size"
diff --git a/packages/SystemUI/res/drawable/stat_sys_branded_vpn.xml b/packages/SystemUI/res/drawable/stat_sys_branded_vpn.xml
new file mode 100644
index 0000000..a86e5b9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_branded_vpn.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="17.0dp"
+ android:height="17.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12.700000,10.000000c-0.800000,-2.300000 -3.000000,-4.000000 -5.700000,-4.000000c-3.300000,0.000000 -6.000000,2.700000 -6.000000,6.000000s2.700000,6.000000 6.000000,6.000000c2.600000,0.000000 4.800000,-1.700000 5.700000,-4.000000L17.000000,14.000000l0.000000,4.000000l4.000000,0.000000l0.000000,-4.000000l2.000000,0.000000l0.000000,-4.000000L12.700000,10.000000zM7.000000,14.000000c-1.100000,0.000000 -2.000000,-0.900000 -2.000000,-2.000000c0.000000,-1.100000 0.900000,-2.000000 2.000000,-2.000000s2.000000,0.900000 2.000000,2.000000C9.000000,13.100000 8.100000,14.000000 7.000000,14.000000z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g_plus.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g_plus.xml
new file mode 100644
index 0000000..3cdd3e1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g_plus.xml
@@ -0,0 +1,30 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="17.0dp"
+ android:height="17.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M4.6,7.8l0.7,0.0l0.0,1.3L4.6,9.1L4.6,11.0L3.0,11.0L3.0,9.2L0.1,9.2L0.0,8.2l3.0,-5.7l1.7,0.0L4.6,7.8L4.6,7.8zM1.7,7.8L3.0,7.8l0.0,-3.0L2.9,5.0L1.7,7.8z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M11.9,9.9c-0.2,0.4 -0.6,0.7 -1.0,0.9s-1.0,0.4 -1.8,0.4c-0.9,0.0 -1.7,-0.3 -2.2,-0.8S6.1,9.0 6.1,7.9L6.1,5.6c0.0,-1.1 0.3,-1.9 0.8,-2.4S8.2,2.4 9.0,2.4c1.0,0.0 1.7,0.2 2.1,0.7s0.7,1.2 0.7,2.1l-1.6,0.0c0.0,-0.5 -0.1,-0.9 -0.2,-1.1S9.5,3.7 9.0,3.7c-0.4,0.0 -0.7,0.2 -0.9,0.5S7.8,5.0 7.8,5.6l0.0,2.3c0.0,0.7 0.1,1.1 0.3,1.4c0.2,0.3 0.6,0.5 1.0,0.5c0.3,0.0 0.6,0.0 0.7,-0.1s0.3,-0.2 0.4,-0.3L10.2,7.8L9.0,7.8L9.0,6.6l2.9,0.0L11.9,9.9z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M17.7,4.4l-1.900001,0.0 0.0,-1.9 -1.2,0.0 0.0,1.9 -1.900001,0.0 0.0,1.2 1.900001,0.0 0.0,1.9 1.2,0.0 0.0,-1.9 1.900001,0.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte_plus.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte_plus.xml
new file mode 100644
index 0000000..db18fad
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte_plus.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="17.0dp"
+ android:height="17.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M2.0,9.7l2.0,0.0L4.0,11.0L0.4,11.0L0.4,2.5L2.0,2.5L2.0,9.7z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M8.3,3.8L7.0,3.8L7.0,11.0L5.4,11.0L5.4,3.8L4.0,3.8L4.0,2.5l4.3,0.0L8.3,3.8z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12.4,7.3l-1.7,0.0l0.0,2.4l2.1,0.0L12.799999,11.0L9.0,11.0L9.0,2.5l3.7,0.0l0.0,1.3l-2.1,0.0l0.0,2.1l1.7,0.0L12.4,7.3L12.4,7.3z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M18.4,4.4l-1.9,0.0 0.0,-1.9 -1.2,0.0 0.0,1.9 -1.900001,0.0 0.0,1.2 1.900001,0.0 0.0,1.9 1.2,0.0 0.0,-1.9 1.9,0.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/switchbar_background.xml b/packages/SystemUI/res/drawable/switchbar_background.xml
index 8d97c46..fb59633 100644
--- a/packages/SystemUI/res/drawable/switchbar_background.xml
+++ b/packages/SystemUI/res/drawable/switchbar_background.xml
@@ -16,6 +16,6 @@
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:attr/colorControlHighlight">
- <item android:drawable="@color/switch_bar_background" />
+ <item android:drawable="?android:attr/colorSecondary" />
</ripple>
diff --git a/packages/SystemUI/res/drawable/volume_dialog_background.xml b/packages/SystemUI/res/drawable/volume_dialog_background.xml
index e98bfb8..d6adea9 100644
--- a/packages/SystemUI/res/drawable/volume_dialog_background.xml
+++ b/packages/SystemUI/res/drawable/volume_dialog_background.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
- <solid android:color="@color/system_primary_color" />
+ <solid android:color="?android:attr/colorPrimary" />
</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/zen_introduction_message_background.xml b/packages/SystemUI/res/drawable/zen_introduction_message_background.xml
index 352fb57..e5b41a3 100644
--- a/packages/SystemUI/res/drawable/zen_introduction_message_background.xml
+++ b/packages/SystemUI/res/drawable/zen_introduction_message_background.xml
@@ -17,6 +17,6 @@
<corners android:radius="@dimen/borderless_button_radius" />
- <solid android:color="@color/zen_introduction_message_background" />
+ <solid android:color="?android:attr/colorAccent" />
</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml b/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml
index ba5c0aa..a726161 100644
--- a/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml
+++ b/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml
@@ -20,7 +20,7 @@
android:id="@+id/date_time_alarm_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
+ android:layout_marginTop="16dp"
android:layout_marginStart="16dp"
android:gravity="start"
android:orientation="vertical">
@@ -62,7 +62,8 @@
<com.android.systemui.statusbar.AlphaOptimizedButton
android:id="@+id/alarm_status"
android:layout_width="wrap_content"
- android:layout_height="20dp"
+ android:layout_height="wrap_content"
+ android:minHeight="20dp"
android:paddingTop="3dp"
android:drawablePadding="8dp"
android:drawableStart="@drawable/ic_access_alarms_small"
diff --git a/packages/SystemUI/res/layout/battery_detail.xml b/packages/SystemUI/res/layout/battery_detail.xml
index 1f24ab0..8abfcf6 100644
--- a/packages/SystemUI/res/layout/battery_detail.xml
+++ b/packages/SystemUI/res/layout/battery_detail.xml
@@ -25,7 +25,7 @@
android:id="@+id/charge_and_estimation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingStart="72dp"
+ android:paddingStart="16dp"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/colorAccent" />
diff --git a/packages/SystemUI/res/layout/calibrate_sliders.xml b/packages/SystemUI/res/layout/calibrate_sliders.xml
deleted file mode 100644
index 0dec8a1..0000000
--- a/packages/SystemUI/res/layout/calibrate_sliders.xml
+++ /dev/null
@@ -1,97 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2015 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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:orientation="vertical">
-
- <LinearLayout
- android:id="@+id/r_group"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"
- android:orientation="horizontal">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/color_modification_r"
- android:gravity="center"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <SeekBar android:id="@*android:id/seekbar"
- android:layout_marginStart="16dp"
- android:layout_gravity="center_vertical"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1" />
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/g_group"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"
- android:orientation="horizontal">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/color_modification_g"
- android:gravity="center"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <SeekBar android:id="@*android:id/seekbar"
- android:layout_marginStart="16dp"
- android:layout_gravity="center_vertical"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1" />
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/b_group"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"
- android:orientation="horizontal">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/color_modification_b"
- android:gravity="center"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <SeekBar android:id="@*android:id/seekbar"
- android:layout_marginStart="16dp"
- android:layout_gravity="center_vertical"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1" />
- </LinearLayout>
-
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
index 52cab72..69e52c5 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
@@ -53,5 +53,6 @@
android:layout_alignParentEnd="true"
android:textSize="14sp"
android:scrollHorizontally="false"
- android:layout_centerVertical="true"/>
+ android:layout_centerVertical="true"
+ android:focusable="true"/>
</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
index 5db6789..a3901d0 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
@@ -21,4 +21,4 @@
android:padding="@dimen/ksh_item_padding"
android:layout_marginStart="@dimen/ksh_item_margin_start"
android:scaleType="fitXY"
- android:background="@drawable/ksh_key_item_background"/>
+ android:background="@drawable/ksh_key_item_background" />
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
index 31a8773..b06f7fc 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
@@ -23,4 +23,5 @@
android:textColor="@color/ksh_key_item_color"
android:singleLine="true"
android:gravity="center"
- android:textSize="@dimen/ksh_item_text_size"/>
+ android:textSize="@dimen/ksh_item_text_size"
+ android:textAllCaps="true"/>
diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml
index d09c42b..e84ed23 100644
--- a/packages/SystemUI/res/layout/notification_guts.xml
+++ b/packages/SystemUI/res/layout/notification_guts.xml
@@ -26,7 +26,8 @@
android:orientation="vertical"
android:paddingStart="@*android:dimen/notification_content_margin_start"
android:paddingEnd="8dp"
- android:background="@color/notification_guts_bg_color">
+ android:background="@color/notification_guts_bg_color"
+ android:theme="@*android:style/Theme.DeviceDefault.Light">
<!-- header -->
<LinearLayout
@@ -145,8 +146,8 @@
android:focusable="true"
android:background="#00ffffff"
android:progressBackgroundTint="@color/notification_guts_secondary_slider_color"
- android:thumbTint="@color/notification_guts_slider_color"
- android:progressTint="@color/notification_guts_slider_color"
+ android:thumbTint="?android:attr/colorAccent"
+ android:progressTint="?android:attr/colorAccent"
style="@android:style/Widget.Material.SeekBar.Discrete"
android:tickMarkTint="@android:color/black" />
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index 63390e2..967db26 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -47,11 +47,12 @@
<com.android.systemui.qs.NonInterceptingScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_weight="1">
+ android:layout_weight="1"
+ android:fillViewport="true">
<FrameLayout
android:id="@android:id/content"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="match_parent"/>
</com.android.systemui.qs.NonInterceptingScrollView>
<include layout="@layout/qs_detail_buttons" />
diff --git a/packages/SystemUI/res/layout/qs_detail_header.xml b/packages/SystemUI/res/layout/qs_detail_header.xml
index c062b6d..f6ca862 100644
--- a/packages/SystemUI/res/layout/qs_detail_header.xml
+++ b/packages/SystemUI/res/layout/qs_detail_header.xml
@@ -22,19 +22,9 @@
android:background="@drawable/btn_borderless_rect"
android:gravity="center">
- <ImageView
- android:id="@*android:id/up"
- android:layout_width="56dp"
- android:layout_height="56dp"
- android:layout_marginEnd="16dp"
- android:padding="16dp"
- android:clickable="true"
- android:background="?android:attr/selectableItemBackground"
- android:contentDescription="@*android:string/action_bar_up_description"
- android:src="?android:attr/homeAsUpIndicator" />
-
<TextView
android:id="@android:id/title"
+ android:paddingStart="16dp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
diff --git a/packages/SystemUI/res/layout/qs_detail_items.xml b/packages/SystemUI/res/layout/qs_detail_items.xml
index f1a8d63..c94a972 100644
--- a/packages/SystemUI/res/layout/qs_detail_items.xml
+++ b/packages/SystemUI/res/layout/qs_detail_items.xml
@@ -15,7 +15,8 @@
limitations under the License.
-->
<!-- extends FrameLayout -->
-<com.android.systemui.qs.QSDetailItems xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.systemui.qs.QSDetailItems
+ xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -28,26 +29,26 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- sysui:itemHeight="@dimen/qs_detail_item_height" />
+ sysui:itemHeight="@dimen/qs_detail_item_height"/>
<LinearLayout
android:id="@android:id/empty"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:layout_gravity="center"
- android:gravity="center_horizontal"
- android:orientation="vertical" >
+ android:gravity="center"
+ android:orientation="vertical">
<ImageView
android:id="@android:id/icon"
android:layout_width="56dp"
- android:layout_height="56dp" />
+ android:layout_height="56dp"/>
<TextView
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
- android:textAppearance="@style/TextAppearance.QS.DetailEmpty" />
+ android:textAppearance="@style/TextAppearance.QS.DetailEmpty"/>
</LinearLayout>
</com.android.systemui.qs.QSDetailItems>
diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
index ee55ec2..8b6060f 100644
--- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
+++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
@@ -37,19 +37,6 @@
android:importantForAccessibility="yes"
android:focusable="true" />
- <TextView
- android:id="@android:id/edit"
- style="@style/QSBorderlessButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end"
- android:minWidth="88dp"
- android:textAppearance="@style/TextAppearance.QS.DetailButton"
- android:textColor="#64FFFFFF"
- android:focusable="true"
- android:text="@string/qs_edit"
- android:contentDescription="@string/accessibility_quick_settings_edit"/>
-
</FrameLayout>
</com.android.systemui.qs.PagedTileLayout>
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 751f181..26c7339 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -26,7 +26,7 @@
<com.android.systemui.qs.QSPanel
android:id="@+id/quick_settings_panel"
android:background="#0000"
- android:layout_marginTop="@dimen/status_bar_header_height"
+ android:layout_marginTop="80dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="8dp" />
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index 58fc069..8c6c7cf 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -28,6 +28,7 @@
android:minHeight="112dp"
android:clipChildren="false"
android:clipToPadding="false"
+ android:focusable="true"
android:background="@drawable/ripple_drawable"
systemui:activatedFontFamily="sans-serif-medium">
@@ -65,4 +66,4 @@
android:visibility="gone" />
</LinearLayout>
-</com.android.systemui.qs.tiles.UserDetailItemView>
\ No newline at end of file
+</com.android.systemui.qs.tiles.UserDetailItemView>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index f65a667..6673d6e 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -37,13 +37,14 @@
android:clipToPadding="false"
android:orientation="horizontal"
android:layout_alignParentEnd="true"
- android:layout_marginTop="28dp"
+ android:layout_marginTop="4dp"
android:layout_marginEnd="12dp">
<com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentEnd="true"
+ android:focusable="true"
android:background="@drawable/ripple_drawable" >
<ImageView android:id="@+id/multi_user_avatar"
android:layout_width="@dimen/multi_user_avatar_expanded_size"
@@ -52,6 +53,18 @@
android:scaleType="centerInside"/>
</com.android.systemui.statusbar.phone.MultiUserSwitch>
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@android:id/edit"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:clipToPadding="false"
+ android:clickable="true"
+ android:focusable="true"
+ android:src="@drawable/ic_mode_edit"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:contentDescription="@string/accessibility_quick_settings_edit"
+ android:padding="14dp" />
+
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/settings_button_container"
android:layout_width="48dp"
@@ -64,7 +77,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/ripple_drawable"
- android:src="@drawable/ic_settings"
+ android:src="@drawable/ic_settings_20dp"
android:contentDescription="@string/accessibility_quick_settings_settings" />
<com.android.systemui.statusbar.AlphaOptimizedImageView android:id="@+id/tuner_icon"
android:layout_width="match_parent"
@@ -98,7 +111,7 @@
android:layout_alignParentTop="true"
android:paddingStart="16dp"
android:paddingEnd="16dp"
- android:paddingTop="8dp"
+ android:paddingTop="6dp"
android:visibility="gone"
android:textAppearance="@style/TextAppearance.StatusBar.Expanded.EmergencyCallsOnly"
android:text="@*android:string/emergency_calls_only"
@@ -116,7 +129,7 @@
android:id="@+id/quick_qs_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="28dp"
+ android:layout_marginTop="52dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:layout_alignParentEnd="true"
diff --git a/packages/SystemUI/res/layout/recents_stack_action_button.xml b/packages/SystemUI/res/layout/recents_stack_action_button.xml
index 43b3de1..541000b 100644
--- a/packages/SystemUI/res/layout/recents_stack_action_button.xml
+++ b/packages/SystemUI/res/layout/recents_stack_action_button.xml
@@ -31,6 +31,6 @@
android:shadowDy="2"
android:shadowRadius="5"
android:fontFamily="sans-serif-medium"
- android:background="?android:selectableItemBackground"
+ android:background="@drawable/recents_stack_action_background"
android:visibility="invisible"
android:forceHasOverlappingRendering="false" />
diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml
index 0c8cc9b..f0c4e59 100644
--- a/packages/SystemUI/res/layout/remote_input.xml
+++ b/packages/SystemUI/res/layout/remote_input.xml
@@ -42,7 +42,6 @@
android:singleLine="true"
android:ellipsize="start"
android:inputType="textShortMessage|textAutoCorrect|textCapSentences"
- android:textIsSelectable="true"
android:imeOptions="actionNone|flagNoExtractUi" />
<FrameLayout
diff --git a/packages/SystemUI/res/layout/screen_pinning_request.xml b/packages/SystemUI/res/layout/screen_pinning_request.xml
index fea45cc..d55ba94 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request.xml
@@ -21,7 +21,8 @@
android:layout_height="wrap_content"
android:gravity="bottom|center_horizontal"
android:layoutDirection="ltr"
- android:orientation="vertical" >
+ android:orientation="vertical"
+ android:theme="@android:style/Theme.DeviceDefault.Light">
<include
android:layout_width="@dimen/screen_pinning_request_width"
@@ -32,7 +33,7 @@
android:id="@+id/spacer"
android:layout_width="@dimen/screen_pinning_request_width"
android:layout_height="18dp"
- android:background="@color/screen_pinning_request_bg" />
+ android:background="?android:attr/colorAccent" />
<include
android:layout_width="@dimen/screen_pinning_request_width"
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
index 60112be..e3662f17 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
@@ -28,7 +28,7 @@
android:id="@+id/screen_pinning_buttons"
android:layout_width="match_parent"
android:layout_height="@dimen/screen_pinning_request_button_height"
- android:background="@color/screen_pinning_request_bg" >
+ android:background="?android:attr/colorAccent">
<View
android:layout_width="@dimen/screen_pinning_request_side_width"
@@ -42,7 +42,8 @@
android:layout_height="@dimen/screen_pinning_request_button_height"
android:layout_weight="0"
android:paddingStart="@dimen/screen_pinning_request_frame_padding"
- android:paddingEnd="@dimen/screen_pinning_request_frame_padding" >
+ android:paddingEnd="@dimen/screen_pinning_request_frame_padding"
+ android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
<ImageView
android:id="@+id/screen_pinning_back_bg_light"
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
index ebad7a4..3649ffb 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
@@ -25,8 +25,8 @@
android:id="@+id/screen_pinning_buttons"
android:layout_height="match_parent"
android:layout_width="@dimen/screen_pinning_request_button_height"
- android:background="@color/screen_pinning_request_bg"
- android:orientation="vertical" >
+ android:background="?android:attr/colorAccent"
+ android:orientation="vertical">
<View
android:layout_height="@dimen/screen_pinning_request_side_width"
@@ -82,7 +82,8 @@
android:id="@+id/screen_pinning_back_group"
android:layout_height="@dimen/screen_pinning_request_button_width"
android:layout_width="@dimen/screen_pinning_request_button_height"
- android:layout_weight="0" >
+ android:layout_weight="0"
+ android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
<ImageView
android:id="@+id/screen_pinning_back_bg_light"
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_land_phone.xml b/packages/SystemUI/res/layout/screen_pinning_request_land_phone.xml
index e6c22d4..ec16991 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_land_phone.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_land_phone.xml
@@ -20,7 +20,8 @@
android:layout_height="@dimen/screen_pinning_request_width"
android:layout_width="wrap_content"
android:gravity="right|center_vertical"
- android:orientation="horizontal" >
+ android:orientation="horizontal"
+ android:theme="@android:style/Theme.DeviceDefault.Light">
<include
android:layout_width="360dp"
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_text_area.xml b/packages/SystemUI/res/layout/screen_pinning_request_text_area.xml
index df957f4..cdad94b 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_text_area.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_text_area.xml
@@ -20,8 +20,8 @@
android:id="@+id/screen_pinning_text_area"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:background="@color/screen_pinning_request_bg"
- android:gravity="center_vertical" >
+ android:background="?android:attr/colorAccent"
+ android:gravity="center_vertical">
<TextView
android:id="@+id/screen_pinning_title"
diff --git a/packages/SystemUI/res/layout/status_bar_alarm_group.xml b/packages/SystemUI/res/layout/status_bar_alarm_group.xml
index f94b727..701b621 100644
--- a/packages/SystemUI/res/layout/status_bar_alarm_group.xml
+++ b/packages/SystemUI/res/layout/status_bar_alarm_group.xml
@@ -20,7 +20,7 @@
android:id="@+id/date_time_alarm_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
+ android:layout_marginTop="16dp"
android:layout_marginStart="16dp"
android:gravity="start"
android:orientation="vertical">
@@ -60,7 +60,8 @@
<com.android.systemui.statusbar.AlphaOptimizedButton
android:id="@+id/alarm_status"
android:layout_width="wrap_content"
- android:layout_height="20dp"
+ android:layout_height="wrap_content"
+ android:minHeight="20dp"
android:paddingTop="3dp"
android:drawablePadding="8dp"
android:drawableStart="@drawable/ic_access_alarms_small"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 2c8a559..3d70969 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -65,6 +65,14 @@
layout="@layout/keyguard_status_bar"
android:visibility="invisible" />
+ <Button
+ android:id="@+id/report_rejected_touch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/status_bar_header_height_keyguard"
+ android:text="@string/report_rejected_touch"
+ android:visibility="gone" />
+
</com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer>
<include
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
deleted file mode 100644
index c5cd65e..0000000
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-** Copyright 2012, 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.
--->
-
-<!-- Extends RelativeLayout -->
-<com.android.systemui.statusbar.phone.StatusBarHeaderView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:id="@+id/header"
- android:layout_width="@dimen/notification_panel_width"
- android:layout_height="@dimen/status_bar_header_height"
- android:layout_gravity="@integer/notification_panel_layout_gravity"
- android:baselineAligned="false"
- android:elevation="4dp"
- android:background="@drawable/notification_header_bg"
- android:clickable="true"
- android:focusable="true"
- >
-
- <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch"
- android:layout_width="@dimen/multi_user_switch_width_collapsed"
- android:layout_height="@dimen/status_bar_header_height"
- android:layout_alignParentEnd="true"
- android:background="@drawable/ripple_drawable" >
- <ImageView android:id="@+id/multi_user_avatar"
- android:layout_width="@dimen/multi_user_avatar_expanded_size"
- android:layout_height="@dimen/multi_user_avatar_expanded_size"
- android:layout_gravity="center"
- android:scaleType="centerInside"/>
- </com.android.systemui.statusbar.phone.MultiUserSwitch>
-
- <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
- android:id="@+id/settings_button_container"
- android:layout_width="48dp"
- android:layout_height="@dimen/status_bar_header_height"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:layout_toStartOf="@id/multi_user_switch">
-
- <com.android.systemui.statusbar.phone.SettingsButton android:id="@+id/settings_button"
- style="@android:style/Widget.Material.Button.Borderless"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/ripple_drawable"
- android:src="@drawable/ic_settings"
- android:contentDescription="@string/accessibility_desc_settings" />
- <com.android.systemui.statusbar.AlphaOptimizedImageView android:id="@+id/tuner_icon"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingStart="36dp"
- android:tint="#4DFFFFFF"
- android:tintMode="src_in"
- android:visibility="invisible"
- android:src="@drawable/tuner" />
-
- </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
-
- <LinearLayout android:id="@+id/system_icons_super_container"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/status_bar_header_height"
- android:layout_toStartOf="@id/multi_user_switch"
- android:layout_alignWithParentIfMissing="true"
- android:layout_marginStart="16dp"
- android:background="@drawable/ripple_drawable"
- android:paddingEnd="4dp" >
- <FrameLayout android:id="@+id/system_icons_container"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/status_bar_height"
- android:layout_gravity="center_vertical"
- >
- <include layout="@layout/system_icons" />
- </FrameLayout>
- <TextView android:id="@+id/battery_level"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginStart="@dimen/header_battery_margin_expanded"
- android:paddingEnd="@dimen/battery_level_padding_end"
- android:textColor="#ffffff"
- android:textSize="@dimen/battery_level_text_size"
- android:importantForAccessibility="noHideDescendants"/>
- </LinearLayout>
-
- <TextView
- android:id="@+id/header_emergency_calls_only"
- android:layout_height="@dimen/status_bar_header_height"
- android:layout_width="wrap_content"
- android:layout_alignParentStart="true"
- android:layout_toStartOf="@id/system_icons_super_container"
- android:paddingStart="16dp"
- android:paddingEnd="16dp"
- android:visibility="gone"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.EmergencyCallsOnly"
- android:text="@*android:string/emergency_calls_only"
- android:singleLine="true"
- android:gravity="center_vertical" />
-
- <FrameLayout
- android:id="@+id/date_group"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/clock_collapsed_bottom_margin"
- android:layout_alignParentBottom="true">
- <com.android.systemui.statusbar.policy.DateView android:id="@+id/date_collapsed"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:singleLine="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
- android:layout_below="@id/clock"
- systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm"
- />
-
- <com.android.systemui.statusbar.policy.DateView android:id="@+id/date_expanded"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:singleLine="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
- android:layout_below="@id/clock"
- systemui:datePattern="eeeeMMMMd"
- />
- </FrameLayout>
-
- <include layout="@layout/split_clock_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_above="@id/date_group"
- android:id="@+id/clock"
- />
-
- <com.android.systemui.statusbar.AlphaOptimizedButton android:id="@+id/alarm_status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_toEndOf="@id/date_group"
- android:layout_marginBottom="4dp"
- android:drawablePadding="6dp"
- android:drawableStart="@drawable/ic_access_alarms_small"
- android:textColor="#64ffffff"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
- android:paddingEnd="6dp"
- android:paddingStart="6dp"
- android:paddingTop="16dp"
- android:paddingBottom="16dp"
- android:background="?android:attr/selectableItemBackground"
- android:visibility="gone"
- />
-
- <include
- android:id="@+id/qs_detail_header"
- layout="@layout/qs_detail_header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- />
-
- <com.android.systemui.statusbar.AlphaOptimizedImageView
- android:id="@+id/qs_detail_header_progress"
- android:src="@drawable/indeterminate_anim"
- android:alpha="0"
- android:background="@color/qs_detail_progress_track"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- />
-
- <TextView
- android:id="@+id/header_debug_info"
- android:visibility="invisible"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:fontFamily="sans-serif-condensed"
- android:textSize="11dp"
- android:textStyle="bold"
- android:textColor="#00A040"
- android:padding="2dp"
- />
-
-</com.android.systemui.statusbar.phone.StatusBarHeaderView>
diff --git a/packages/SystemUI/res/layout/switch_bar.xml b/packages/SystemUI/res/layout/switch_bar.xml
index f98de96..41cdb78 100644
--- a/packages/SystemUI/res/layout/switch_bar.xml
+++ b/packages/SystemUI/res/layout/switch_bar.xml
@@ -22,7 +22,8 @@
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:clickable="true"
- android:gravity="center">
+ android:gravity="center"
+ android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
<TextView android:id="@+id/switch_text"
android:layout_height="wrap_content"
@@ -42,7 +43,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
- android:background="@null"
- android:theme="@style/ThemeOverlay.SwitchBar" />
+ android:background="@null" />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tile_listing.xml b/packages/SystemUI/res/layout/tile_listing.xml
deleted file mode 100644
index 9ab62ca..0000000
--- a/packages/SystemUI/res/layout/tile_listing.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ** Copyright 2015, 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.
- -->
-
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@drawable/qs_background_primary"
- android:paddingBottom="20dp"
- android:orientation="vertical">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@drawable/notification_header_bg"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:paddingTop="10dp"
- android:paddingBottom="10dp"
- android:orientation="horizontal">
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="36dp"
- android:layout_height="36dp" />
- <TextView
- android:id="@android:id/title"
- android:paddingStart="10dp"
- android:textColor="@android:color/white"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical" />
- </LinearLayout>
-
- <GridLayout
- android:id="@+id/tile_grid"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:columnCount="4" />
-
- </LinearLayout>
diff --git a/packages/SystemUI/res/layout/tuner_qs.xml b/packages/SystemUI/res/layout/tuner_qs.xml
deleted file mode 100644
index 9a51e0c..0000000
--- a/packages/SystemUI/res/layout/tuner_qs.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2015 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.
--->
-
-<com.android.systemui.tuner.AutoScrollView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@color/system_secondary_color" >
- <LinearLayout
- android:id="@+id/all_details"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:gravity="center"
- android:orientation="vertical">
-
- <View
- android:background="@color/qs_tile_divider"
- android:layout_width="match_parent"
- android:layout_height="2dp" />
-
- <FrameLayout
- android:id="@+id/remove_target"
- android:layout_width="105dp"
- android:layout_height="@dimen/qs_tile_height" />
-
- <FrameLayout
- android:id="@+id/add_target"
- android:layout_width="105dp"
- android:layout_height="@dimen/qs_tile_height" />
- </LinearLayout>
-</com.android.systemui.tuner.AutoScrollView>
diff --git a/packages/SystemUI/res/layout/tv_pip_controls.xml b/packages/SystemUI/res/layout/tv_pip_controls.xml
index 0a2f320..49119fb 100644
--- a/packages/SystemUI/res/layout/tv_pip_controls.xml
+++ b/packages/SystemUI/res/layout/tv_pip_controls.xml
@@ -41,5 +41,6 @@
android:layout_height="wrap_content"
android:layout_marginStart="-50dp"
android:src="@drawable/ic_pause_white_24dp"
- android:text="@string/pip_pause" />
+ android:text="@string/pip_pause"
+ android:visibility="gone" />
</merge>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index d3f2a25..b3ff5d6 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -20,19 +20,27 @@
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/volume_dialog_margin_bottom"
android:background="@drawable/volume_dialog_background"
- android:translationZ="4dp"
- android:paddingTop="8dp">
+ android:translationZ="4dp" >
<LinearLayout
android:id="@+id/volume_dialog_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingBottom="8dp"
- android:paddingStart="8dp"
- android:animateLayoutChanges="true" >
+ android:orientation="vertical" >
- <!-- volume rows added and removed here! :-) -->
+ <LinearLayout
+ android:id="@+id/volume_dialog_rows"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingEnd="@dimen/volume_button_size"
+ android:paddingTop="@dimen/volume_dialog_collapsed_padding_top"
+ android:orientation="vertical" >
+ <View android:id="@+id/spacer"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/volume_dialog_expanded_spacer"
+ android:visibility="gone"/>
+ <!-- volume rows added and removed here! :-) -->
+ </LinearLayout>
<include layout="@layout/volume_zen_footer" />
@@ -50,8 +58,11 @@
android:clickable="true"
android:soundEffectsEnabled="false"
android:src="@drawable/ic_volume_collapse_animation"
+ android:background="@drawable/ripple_drawable"
tools:ignore="RtlHardcoded"
android:layout_alignParentEnd="true"
- android:layout_alignParentTop="true"/>
+ android:layout_alignParentTop="true"
+ android:layout_marginTop="@dimen/volume_expander_margin_top"
+ android:layout_marginEnd="@dimen/volume_expander_margin_end"/>
</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index be05a3a..7328d05 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -13,14 +13,14 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<RelativeLayout
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="@dimen/volume_row_height"
android:clipChildren="false"
android:clipToPadding="false"
- android:id="@+id/volume_dialog_row"
- android:paddingEnd="@dimen/volume_button_size" >
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/volume_row_padding_bottom" >
<TextView
android:id="@+id/volume_row_header"
@@ -29,32 +29,28 @@
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.Volume.Header"
- android:paddingBottom="0dp"
- android:paddingEnd="12dp"
- android:paddingStart="12dp"
- android:paddingTop="4dp"
- android:visibility="gone" />
+ android:paddingStart="@dimen/volume_row_header_padding_start" />
- <com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/volume_row_icon"
- style="@style/VolumeButtons"
- android:layout_width="@dimen/volume_button_size"
- android:layout_height="@dimen/volume_button_size"
- android:layout_below="@id/volume_row_header"
- android:soundEffectsEnabled="false" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/volume_row_slider_height"
+ android:orientation="horizontal"
+ android:paddingStart="@dimen/volume_row_padding_start" >
+ <com.android.keyguard.AlphaOptimizedImageButton
+ android:id="@+id/volume_row_icon"
+ style="@style/VolumeButtons"
+ android:layout_width="@dimen/volume_button_size"
+ android:layout_height="@dimen/volume_button_size"
+ android:soundEffectsEnabled="false" />
- <SeekBar
- android:id="@+id/volume_row_slider"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignBottom="@+id/volume_row_icon"
- android:layout_alignWithParentIfMissing="true"
- android:layout_below="@id/volume_row_header"
- android:layout_toEndOf="@id/volume_row_icon"
- android:layout_toStartOf="@+id/volume_settings_button"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:paddingEnd="8dp"
- android:paddingStart="8dp" />
+ <SeekBar
+ android:id="@+id/volume_row_slider"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignWithParentIfMissing="true"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:paddingStart="@dimen/volume_row_slider_padding_start"/>
+ </LinearLayout>
-</RelativeLayout>
\ No newline at end of file
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_zen_footer.xml b/packages/SystemUI/res/layout/volume_zen_footer.xml
index f30023d..775b157 100644
--- a/packages/SystemUI/res/layout/volume_zen_footer.xml
+++ b/packages/SystemUI/res/layout/volume_zen_footer.xml
@@ -18,7 +18,8 @@
android:id="@+id/volume_zen_footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical" > <!-- extends LinearLayout -->
+ android:orientation="vertical"
+ android:paddingBottom="8dp" > <!-- extends LinearLayout -->
<View
android:id="@+id/zen_embedded_divider"
@@ -78,7 +79,7 @@
android:paddingStart="15dp"
android:paddingEnd="15dp"
android:text="@string/volume_zen_end_now"
- android:textColor="@color/system_accent_color"
+ android:textColor="?android:attr/colorAccent"
android:textAppearance="@style/TextAppearance.QS.DetailButton" />
</com.android.systemui.volume.ZenFooter>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
index 906b867..c0be676 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -40,7 +40,8 @@
android:layout_marginEnd="16dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
- android:background="@drawable/zen_introduction_message_background" >
+ android:background="@drawable/zen_introduction_message_background"
+ android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent.Light">
<ImageView
android:id="@+id/zen_introduction_confirm"
diff --git a/packages/SystemUI/res/values-af/config.xml b/packages/SystemUI/res/values-af/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-af/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index d9138ef..546698c 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Swerwing"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"EDGE"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g>-limiet"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> waarskuwing"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Werkmodus"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Aandbeligting"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Aandbeligting is aan, tik om af te skakel"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Aandbeligting is af, tik om aan te skakel"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Geen onlangse items nie"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Jy het alles toegemaak"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Programinligting"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Verdeel horisontaal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Verdeel vertikaal"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Verdeel gepasmaak"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Gelaai"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Laai tans"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> tot vol"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> is die volumedialoog"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Tik om die oorspronklike terug te stel."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Jy gebruik tans jou werkprofiel"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Bel"</item>
+ <item msgid="5997713001067658559">"Stelsel"</item>
+ <item msgid="7858983209929864160">"Laat toestel lui"</item>
+ <item msgid="1850038478268896762">"Media"</item>
+ <item msgid="8265110906352372092">"Wekker"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Tik om te ontdemp."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tik om op vibreer te stel. Toeganklikheidsdienste kan dalk gedemp wees."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Tik om te demp. Toeganklikheidsdienste kan dalk gedemp wees."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Meer instellings"</string>
<string name="notification_done" msgid="5279426047273930175">"Klaar"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g>-kennisgewingkontroles"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Kleur en voorkoms"</string>
- <string name="night_mode" msgid="3540405868248625488">"Nagmodus"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Kalibreer skerm"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Aan"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Af"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Skakel outomaties aan"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Skakel oor na Nagmodus soos gepas vir ligging en tyd van die dag"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Wanneer Nagmodus aan is"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Gebruik donkertema vir Android-bedryfstelsel"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Verstel tint"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Verstel helderheid"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Die donkertema word toegepas op kernareas van Android-bedryfstelsel wat gewoonlik in \'n ligtema gewys word, soos instellings."</string>
- <string name="color_apply" msgid="9212602012641034283">"Pas toe"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Bevestig instellings"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Sommige kleurinstellings kan hierdie toestel onbruikbaar maak. Klik OK om hierdie kleurinstellings te bevestig; andersins sal hierdie instellings ná 10 sekondes teruggestel word."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Batterygebruik"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Batterybespaarder is nie beskikbaar wanneer gelaai word nie"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Batterybespaarder"</string>
diff --git a/packages/SystemUI/res/values-am/config.xml b/packages/SystemUI/res/values-am/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-am/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 69d6642..bd4afe1 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"ኤል ቲ ኢ"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"ውሂብን በማዛወር ላይ"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ገደብ"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"የ<xliff:g id="DATA_LIMIT">%s</xliff:g> ማስጠንቀቂያ"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"የሥራ ሁነታ"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"የምሽት ብርሃን"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"የምሽት ብርሃን በርቷል፣ ለማጥፋት መታ ያድርጉ"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"የምሽት ብርሃን ጠፍቷል፣ ለማብራት መታ ያድርጉ"</string>
<string name="recents_empty_message" msgid="808480104164008572">"ምንም የቅርብ ጊዜ ንጥሎች የሉም"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"ሁሉንም ነገር አጽድተዋል"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"የመተግበሪያ መረጃ"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"አግድም ክፈል"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ቁልቁል ክፈል"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"በብጁ ክፈል"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"ባትሪ ሞልቷል"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"ኃይል በመሙላት ላይ"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> እስኪሞላ ድረስ"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> የድምጽ መጠን መገናኛው ነው"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"የመጀመሪያውን ወደነበረበት ለመመለስ መታ ያድርጉ።"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"የስራ መገለጫዎን እየተጠቀሙ ነው"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"ደውል"</item>
+ <item msgid="5997713001067658559">"ሥርዓት"</item>
+ <item msgid="7858983209929864160">"ጥሪ"</item>
+ <item msgid="1850038478268896762">"ማህደረመረጃ"</item>
+ <item msgid="8265110906352372092">"ማንቂያ"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"ብሉቱዝ"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s። ድምጸ-ከል ለማድረግ መታ ያድርጉ"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s። ወደ ንዝረት ለማቀናበር መታ ያድርጉ። የተደራሽነት አገልግሎቶች ድምጸ-ከል ሊደረግባቸው ይችላል።"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s። ድምጸ-ከል ለማድረግ መታ ያድርጉ። የተደራሽነት አገልግሎቶች ድምጸ-ከል ሊደረግባቸው ይችላል።"</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"ተጨማሪ ቅንብሮች"</string>
<string name="notification_done" msgid="5279426047273930175">"ተከናውኗል"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> ማሳወቂያ ቁጥጥሮች"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"ቀለም እና መልክ"</string>
- <string name="night_mode" msgid="3540405868248625488">"የሌሊት ሁነታ"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"ማሳያን ይለኩ"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"በርቷል"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"ጠፍቷል"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"በራስ-ሰር አብራ"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"ለአካባቢው እና ለሰዓቱ ተገቢ በሆነ መልኩ ወደ የማታ ሁነታ ለውጥ"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"የማታ ሁነታ ሲበራ"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"ለAndroid ስርዓተ ክወና ጨለማ ገጽታን ተጠቀም"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"ቅልም አስተካክል"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"ብሩህነት አስተካክል"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"ጨለማ ገጽታው እንደ ቅንብሮች ያሉ በመደበኛነት በብርሃን ገጽታ በሚታዩ የAndroid ስርዓተ ክወና ዋና ክፍሎች ላይ ይተገበራል።"</string>
- <string name="color_apply" msgid="9212602012641034283">"ተግብር"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"ቅንብሮችን ያረጋግጡ"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"አንዳንድ የቀለም ቅንብሮች ይህን መሣሪያ የማይጠቅም ሊያደርጉት ይችላሉ። እነዚህን የቀለም ቅንብሮች ለማረጋገጥ እሺ የሚለውን ጠቅ ያድርጉ፣ አለበለዚያ እነዚህ ቅንብሮች ከ10 ሰከንዶች በኋላ ዳግም ይጀምራሉ።"</string>
<string name="battery_panel_title" msgid="7944156115535366613">"የባትሪ አጠቃቀም"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ኃይል በሚሞላበት ጊዜ ባትሪ ቆጣቢ አይገኝም"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"ባትሪ ቆጣቢ"</string>
diff --git a/packages/SystemUI/res/values-ar/config.xml b/packages/SystemUI/res/values-ar/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-ar/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index d7962de..eca668e 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -147,7 +147,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"شبكة الجيل الثالث"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"شبكة 3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"شبكة الجيل الرابع"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"شبكة الجيل الرابع أو أحدث"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"تجوال"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -325,6 +327,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"قيد <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"تحذير <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"وضع العمل"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"إضاءة ليلية"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"الإضاءة الليلية قيد العمل، انقر لإيقافها."</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"الإضاءة الليلية قيد الإيقاف، انقر لتشغيلها."</string>
<string name="recents_empty_message" msgid="808480104164008572">"ليست هناك عناصر تم استخدامها مؤخرًا"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"لقد محوتَ كل شيء"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"معلومات التطبيق"</string>
@@ -338,6 +343,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"تقسيم أفقي"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"تقسيم رأسي"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"تقسيم مخصص"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"تم الشحن"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"جارٍ الشحن"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> حتى الاكتمال"</string>
@@ -442,6 +449,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> هو مربع حوار مستوى الصوت"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"انقر لاستعادة النسخة الأصلية."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"أنت تستخدم ملفك الشخصي للعمل"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"الاتصال"</item>
+ <item msgid="5997713001067658559">"النظام"</item>
+ <item msgid="7858983209929864160">"الرنين"</item>
+ <item msgid="1850038478268896762">"الوسائط"</item>
+ <item msgid="8265110906352372092">"المنبه"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"البلوتوث"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. انقر لإلغاء التجاهل."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. انقر للتعيين على الاهتزاز. قد يتم تجاهل خدمات إمكانية الوصول."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. انقر للتجاهل. قد يتم تجاهل خدمات إمكانية الوصول."</string>
@@ -512,21 +531,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"المزيد من الإعدادات"</string>
<string name="notification_done" msgid="5279426047273930175">"تم"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"عناصر التحكم في إشعارات <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"اللون والمظهر"</string>
- <string name="night_mode" msgid="3540405868248625488">"الوضع الليلي"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"معايرة الشاشة"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"تشغيل"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"إيقاف"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"التشغيل تلقائيًا"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"التبديل إلى الوضع الليلي بما يتناسب مع الموقع والوقت من اليوم"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"عند تشغيل الوضع الليلي"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"استخدام مظهر معتم لنظام التشغيل Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"ضبط التلوين الخفيف"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"ضبط السطوع"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"يتم تطبيق المظهر المعتم على المناطق الأساسية في نظام التشغيل Android والتي يتم عرضها عادة في مظهر مضيء، مثل الإعدادات."</string>
- <string name="color_apply" msgid="9212602012641034283">"تطبيق"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"تأكيد الإعدادات"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"يمكن أن تتسبب بعض إعدادات الألوان في تعطيل إمكانية استخدام الجهاز. يمكنك النقر على \"موافق\" لتأكيد إعدادات الألوان هذه، وإلا فستتم إعادة تعيين هذه الإعدادات بعد ۱۰ ثوانٍ."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"استخدام البطارية"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"وضع توفير شحن البطارية غير متاح أثناء الشحن."</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"توفير شحن البطارية"</string>
diff --git a/packages/SystemUI/res/values-az-rAZ/config.xml b/packages/SystemUI/res/values-az-rAZ/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-az-rAZ/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-az-rAZ/strings.xml b/packages/SystemUI/res/values-az-rAZ/strings.xml
index acb58ce..3d9cc4a 100644
--- a/packages/SystemUI/res/values-az-rAZ/strings.xml
+++ b/packages/SystemUI/res/values-az-rAZ/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Rominq"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> limit"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> xəbərdarlığı"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"İş rejimi"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Gecə işığı"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Gecə işığı aktivdir, deaktiv etmək üçün tıklayın"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Gecə işığı deaktivdir, aktiv etmək üçün tıklayın"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Son elementlər yoxdur"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Hərşeyi təmizlədiniz"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Tətbiq haqqında"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Üfüqi Böl"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Şaquli Böl"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Fərdi Böl"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Dolub"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Enerji doldurulur"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> dolana kimi"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> proqramı səs səviyyəsi dialoqudur"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Orijinalı bərpa etmək üçün tıklayın."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"İş profilinizi istifadə edirsiniz"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Çağrı"</item>
+ <item msgid="5997713001067658559">"Sistem"</item>
+ <item msgid="7858983209929864160">"Zəng"</item>
+ <item msgid="1850038478268896762">"Media"</item>
+ <item msgid="8265110906352372092">"Siqnal"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Səsli etmək üçün tıklayın."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Vibrasiyanı ayarlamaq üçün tıklayın. Əlçatımlılıq xidmətləri səssiz edilmiş ola bilər."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Səssiz etmək üçün tıklayın. Əlçatımlılıq xidmətləri səssiz edilmiş ola bilər."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Daha çox ayar"</string>
<string name="notification_done" msgid="5279426047273930175">"Hazırdır"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> bildiriş nəzarəti"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Rəng və görünüş"</string>
- <string name="night_mode" msgid="3540405868248625488">"Gecə rejimi"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Ekranı kalibrləyin"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Aktiv"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Deaktiv"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Avtomatik aktivləşdirin"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Məkana və günün vaxtına uyğun olaraq Gecə Rejiminə keçin"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Gecə Rejimi aktiv olduqda"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android OS üçün tünd tema istifadə edin"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Çaları nizamlayın"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Parlaqlığı nizamlayın"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Tünd tema Android sisteminin adətən açıq tonda göstərilən əsas elementlərinə (məsələn, \"Ayarlar\") tətbiq edilir."</string>
- <string name="color_apply" msgid="9212602012641034283">"Tətbiq edin"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Ayarları təsdiq edin"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Bəzi renk ayarları bu cihazı yararsız edə bilər. Bu rənk ayarlarını təsdiq etmək üçün OK basın, əks halda bu ayarlar 10 saniyə sonra sıfırlanacaq."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Batareya istifadəsi"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Enerji Qənaəti doldurulma zamanı əlçatan deyil"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Enerji Qənaəti"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/config.xml b/packages/SystemUI/res/values-b+sr+Latn/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-b+sr+Latn/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index f944e4b..f95045c 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -144,7 +144,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -319,6 +321,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Ograničenje od <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Upozorenje za <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Režim rada"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Noćno svetlo"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Noćno svetlo je uključeno, dodirnite da biste ga isključili"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Noćno svetlo je isključeno, dodirnite da biste ga uključili"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Nema nedavnih stavki"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Obrisali ste sve"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacije o aplikaciji"</string>
@@ -332,6 +337,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podeli horizontalno"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Podeli vertikalno"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Prilagođeno deljenje"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Napunjena je"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Punjenje"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> dok se ne napuni"</string>
@@ -436,6 +443,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> je dijalog za jačinu zvuka"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Dodirnite da biste vratili original."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Koristite profil za Work"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Pozovi"</item>
+ <item msgid="5997713001067658559">"Sistem"</item>
+ <item msgid="7858983209929864160">"Prsten"</item>
+ <item msgid="1850038478268896762">"Medijumi"</item>
+ <item msgid="8265110906352372092">"Alarm"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Dodirnite da biste uključili zvuk."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Dodirnite da biste podesili na vibraciju. Zvuk usluga pristupačnosti će možda biti isključen."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Dodirnite da biste isključili zvuk. Zvuk usluga pristupačnosti će možda biti isključen."</string>
@@ -506,21 +525,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Još podešavanja"</string>
<string name="notification_done" msgid="5279426047273930175">"Gotovo"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Kontrole obaveštenja za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Boja i izgled"</string>
- <string name="night_mode" msgid="3540405868248625488">"Noćni režim"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Kalibrišite ekran"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Uključeno"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Isključeno"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Automatski uključi"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Pređite na noćni režim u zavisnosti od lokacije i doba dana"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Kada je noćni režim uključen"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Koristi tamnu temu za Android OS"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Prilagodi senku"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Prilagodi osvetljenost"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Tamna tema se primenjuje na ključne delove Android OS-a koji se obično prikazuju u svetloj temi, poput Podešavanja."</string>
- <string name="color_apply" msgid="9212602012641034283">"Primeni"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Potvrdite podešavanja"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Neka podešavanja boja mogu da učine uređaj neupotrebljivim. Kliknite na Potvrdi da biste potvrdili ova podešavanja boja, pošto će se u suprotnom ova podešavanja resetovati nakon 10 sekundi."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Potrošnja baterije"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Ušteda baterije nije dostupna tokom punjenja"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Ušteda baterije"</string>
diff --git a/packages/SystemUI/res/values-be-rBY/config.xml b/packages/SystemUI/res/values-be-rBY/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-be-rBY/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-be-rBY/strings.xml b/packages/SystemUI/res/values-be-rBY/strings.xml
index 12ba0cd..325d706 100644
--- a/packages/SystemUI/res/values-be-rBY/strings.xml
+++ b/packages/SystemUI/res/values-be-rBY/strings.xml
@@ -145,7 +145,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Роўмінг"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -323,6 +325,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Ліміт <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Папярэджанне: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Рэжым працы"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Начная падсветка"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"\"Начная падсветка\" ўключана; дакраніцеся, каб выключыць"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"\"Начная падсветка\" выключана; дакраніцеся, каб уключыць"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Няма нядаўніх элементаў"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Вы ачысцілі усё"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Звесткі аб праграме"</string>
@@ -336,6 +341,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Падзяліць гарызантальна"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Падзяліць вертыкальна"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Падзяліць іншым чынам"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Зараджаны"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Зарадка"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> да поўнай зарадкі"</string>
@@ -350,8 +357,7 @@
<string name="zen_silence_introduction" msgid="3137882381093271568">"Гэта заблакіруе ЎСЕ гукі і вібрацыі, у тым ліку ад будзільнікаў, музыкі, відэа і гульняў."</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Менш тэрміновыя апавяшчэнні ніжэй"</string>
- <!-- no translation found for notification_tap_again (7590196980943943842) -->
- <skip />
+ <string name="notification_tap_again" msgid="7590196980943943842">"Дакраніцеся яшчэ раз, каб адкрыць"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Правядзіце пальцам уверх, каб разблакіраваць"</string>
<string name="phone_hint" msgid="4872890986869209950">"Тэлефон: правядзіце пальцам ад значка"</string>
<string name="voice_hint" msgid="8939888732119726665">"Галасавая дапамога: правядзіце пальцам ад значка"</string>
@@ -441,6 +447,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> з\'яўляецца дыялогам гучнасці"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Дакраніцеся, каб аднавіць арыгінал."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Вы выкарыстоўваеце свой працоўны профіль"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Выклік"</item>
+ <item msgid="5997713001067658559">"Сістэма"</item>
+ <item msgid="7858983209929864160">"Званок"</item>
+ <item msgid="1850038478268896762">"Медыя"</item>
+ <item msgid="8265110906352372092">"Будзільнік"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Дакраніцеся, каб уключыць гук."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Дакраніцеся, каб уключыць вібрацыю. Можа быць адключаны гук службаў спецыяльных магчымасцей."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Дакраніцеся, каб адключыць гук. Можа быць адключаны гук службаў спецыяльных магчымасцей."</string>
@@ -511,21 +529,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Дадатковыя налады"</string>
<string name="notification_done" msgid="5279426047273930175">"Гатова"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Элементы кантролю апавяшчэнняў <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Колер і выгляд"</string>
- <string name="night_mode" msgid="3540405868248625488">"Начны рэжым"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Каліброўка дысплэя"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Уключана"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Выключана"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Уключаць аўтаматычна"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Пераключэнне ў начны рэжым у залежнасці ад канкрэтнай мясцовасці і часу сутак"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Калі ўключаны Начны рэжым"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Выкарыстоўваць цёмную тэму для АС Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Рэгуляванне адценняў"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Рэгуляванне яркасці"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Цёмная тэма ўжываецца да асноўных вобласцяў АС Android, да якіх звычайна шжываецца светлая тэма, напрыклад, Налады."</string>
- <string name="color_apply" msgid="9212602012641034283">"Ужыць"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Пацвердзіце налады"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Некаторыя налады колеру могуць зрабіць гэту прыладу непрыдатнай для выкарыстання. Націсніце кнопку «ОК», каб пацвердзіць гэтыя налады колеру, у адваротным выпадку гэтыя налады будуць скінуты праз 10 секунд."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Выкарыстанне зараду"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Эканомія зараду акумулятара недаступная падчас зарадкі"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Эканомія зараду"</string>
diff --git a/packages/SystemUI/res/values-bg/config.xml b/packages/SystemUI/res/values-bg/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-bg/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 8d7e8da..c5817ee 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Роуминг"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Ограничение от <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Предупреждение: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Работен режим"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Нощно осветление"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Функцията за нощно осветление е включена. Докоснете, за да я изключите"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Функцията за нощно осветление е изключена. Докоснете, за да я включите"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Няма скорошни елементи"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Изчистихте всичко"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Информация за приложението"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Хоризонтално разделяне"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Вертикално разделяне"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Персонализирано разделяне"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Заредена"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Зарежда се"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> до пълно зареждане"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> изпълнява ролята на диалоговия прозорец за силата на звука"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Докоснете, за да се възстанови първоначалната стойност."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Използвате служебния си потребителски профил"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Обаждане"</item>
+ <item msgid="5997713001067658559">"Система"</item>
+ <item msgid="7858983209929864160">"Позвъняване"</item>
+ <item msgid="1850038478268896762">"Мултимедия"</item>
+ <item msgid="8265110906352372092">"Будилник"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Докоснете, за да включите отново звука."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Докоснете, за да зададете вибриране. Възможно е звукът на услугите за достъпност да бъде заглушен."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Докоснете, за да заглушите звука. Възможно е звукът на услугите за достъпност да бъде заглушен."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Още настройки"</string>
<string name="notification_done" msgid="5279426047273930175">"Готово"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Контроли за известията от <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Цвят и облик"</string>
- <string name="night_mode" msgid="3540405868248625488">"Нощен режим"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Калибриране на дисплея"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Вкл."</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Изкл."</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Автоматично включване"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Превключване към нощен режим според местоположението и часа от денонощието"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"При включен нощен режим"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Ползв. на тъмната тема за опер. с-ма Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Коригиране на нюансирането"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Коригиране на яркостта"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Тъмната тема се прилага към основните области на операционната система Android, които обикновено се показват със светла тема, като например настройките."</string>
- <string name="color_apply" msgid="9212602012641034283">"Прилагане"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Потвърждаване на настройките"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Някои настройки за цветовете могат да направят това устройство неизползваемо. За да ги потвърдите, кликнете върху „OK“. В противен случай те ще бъдат нулирани след 10 секунди."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Ползв. на батерията"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Режимът за запазване на батерията не е налице при зареждане"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Режим за запазване на батерията"</string>
diff --git a/packages/SystemUI/res/values-bn-rBD/config.xml b/packages/SystemUI/res/values-bn-rBD/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-bn-rBD/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml
index e32fb06..40076e6 100644
--- a/packages/SystemUI/res/values-bn-rBD/strings.xml
+++ b/packages/SystemUI/res/values-bn-rBD/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"রোমিং"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"সীমা <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> সতর্কতা"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"কাজের মোড"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"নাইট লাইট"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"নাইট লাইট চালু আছে, বন্ধ করতে আলতো চাপুন"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"নাইট লাইট বন্ধ আছে, চালু করতে আলতো চাপুন"</string>
<string name="recents_empty_message" msgid="808480104164008572">"কোনো সাম্প্রতিক আইটেম নেই"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"আপনি সবকিছু সাফ করেছেন"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"অ্যাপ্লিকেশানের তথ্য"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"অনুভূমিক স্প্লিট"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"উল্লম্ব স্প্লিট"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"কাস্টম স্প্লিট করুন"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"চার্জ হয়েছে"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"চার্জ হচ্ছে"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"পূর্ণ হতে <xliff:g id="CHARGING_TIME">%s</xliff:g> সময় লাগবে"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> হল ভলিউম ডায়লগ"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"আসলটি পুনঃস্থাপন করতে আলতো চাপ দিন৷"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"আপনি আপনার কাজের প্রোফাইল ব্যবহার করছেন"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"কল করুন"</item>
+ <item msgid="5997713001067658559">"সিস্টেম"</item>
+ <item msgid="7858983209929864160">"রিং"</item>
+ <item msgid="1850038478268896762">"মিডিয়া"</item>
+ <item msgid="8265110906352372092">"অ্যালার্ম"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"ব্লুটুথ"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s। সশব্দ করতে আলতো চাপুন।"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s। কম্পন এ সেট করতে আলতো চাপুন। অ্যাক্সেসযোগ্যতার পরিষেবাগুলিকে নিঃশব্দ করা হতে পারে।"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s। নিঃশব্দ করতে আলতো চাপুন। অ্যাক্সেসযোগ্যতার পরিষেবাগুলিকে নিঃশব্দ করা হতে পারে।"</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"আরো সেটিংস"</string>
<string name="notification_done" msgid="5279426047273930175">"সম্পন্ন"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> বিজ্ঞপ্তির নিয়ন্ত্রণগুলি"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"রঙ এবং চেহারা"</string>
- <string name="night_mode" msgid="3540405868248625488">"রাতের মোড"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"প্রদর্শন ক্যালিব্রেট করুন"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"চালু আছে"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"বন্ধ আছে"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"স্বয়ংক্রিয়ভাবে চালু করুন"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"অবস্থান এবং সময়ের হিসাবে উপযুক্ত রাতের মোডে পাল্টান"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"যখন রাতের মোড চালু থাকবে"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android OS এর জন্য গাঢ় থিম ব্যবহার করুন"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"টিন্ট সমন্বয় করুন"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"উজ্জ্বলতা সমন্বয় করুন"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Android OS এর মূল অংশগুলিতে গাঢ় থিম প্রয়োগ করা হয়েছে যেটা সাধারণত একটি হালকা থিমে প্রদর্শিত হয়, যেমন সেটিংস৷"</string>
- <string name="color_apply" msgid="9212602012641034283">"প্রয়োগ করুন"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"সেটিংস নিশ্চিত করুন"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"কিছু রঙের সেটিংস এই ডিভাইসকে ব্যবহারের অযোগ্য করে দিতে পারে৷ এই রঙের সেটিংস নিশ্চিত করতে ঠিক আছে এ ক্লিক করুন, অন্যথায় ১০ সেকেন্ড পরে এই সেটিংস পুনরায় সেট হবে৷"</string>
<string name="battery_panel_title" msgid="7944156115535366613">"ব্যাটারির ব্যবহার"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"চার্জ করার সময় ব্যাটারি সেভার উপলব্ধ নয়"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"ব্যাটারি সেভার"</string>
diff --git a/packages/SystemUI/res/values-bs-rBA/config.xml b/packages/SystemUI/res/values-bs-rBA/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-bs-rBA/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-bs-rBA/strings.xml b/packages/SystemUI/res/values-bs-rBA/strings.xml
index c0b8985..dcba67b 100644
--- a/packages/SystemUI/res/values-bs-rBA/strings.xml
+++ b/packages/SystemUI/res/values-bs-rBA/strings.xml
@@ -61,11 +61,11 @@
<string name="label_view" msgid="6304565553218192990">"Prikaži"</string>
<string name="always_use_device" msgid="1450287437017315906">"Koristiti kao zadanu opciju za ovaj USB uređaj"</string>
<string name="always_use_accessory" msgid="1210954576979621596">"Koristiti kao zadanu opciju za ovaj USB uređaj"</string>
- <string name="usb_debugging_title" msgid="4513918393387141949">"Omogućiti otklanjanje grešaka preko USB-a?"</string>
+ <string name="usb_debugging_title" msgid="4513918393387141949">"Omogućiti otklanjanje grešaka putem uređaja spojenog na USB?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"RSA otisak prsta za otključavanje računara je: \n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Uvijek dozvoli sa ovog računara"</string>
- <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Uklanjanje pogreški putem USB-a nije dozvoljeno"</string>
- <string name="usb_debugging_secondary_user_message" msgid="8572228137833020196">"Korisnik koji je trenutno prijavljen na uređaju ne može uključiti opciju za otklanjanje grešaka koristeći USB. Da biste koristili ovu funkciju prebacite se na korisnika administratora."</string>
+ <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Otklanjanje grešaka putem uređaja spojenog na USB nije dozvoljeno"</string>
+ <string name="usb_debugging_secondary_user_message" msgid="8572228137833020196">"Korisnik koji je trenutno prijavljen na uređaju ne može uključiti opciju za otklanjanje grešaka putem uređaja spojenog na USB. Da biste koristili ovu funkciju prebacite se na korisnika administratora."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Uvećaj prikaz na ekran"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Razvuci prikaz na ekran"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Spašavanje snimka ekrana..."</string>
@@ -144,7 +144,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -257,7 +259,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekran je zaključan u vodoravnom prikazu."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekran je zaključan u uspravnom prikazu."</string>
<string name="accessibility_rotation_lock_off_changed" msgid="8134601071026305153">"Ekran će se sada automatski rotirati."</string>
- <string name="accessibility_rotation_lock_on_landscape_changed" msgid="3135965553707519743">"Ekran je sada zaključan u vodoravnom položaju."</string>
+ <string name="accessibility_rotation_lock_on_landscape_changed" msgid="3135965553707519743">"Ekran je sada zaključan u vodoravnom prikazu."</string>
<string name="accessibility_rotation_lock_on_portrait_changed" msgid="8922481981834012126">"Ekran je sada zaključan u uspravnom položaju."</string>
<string name="dessert_case" msgid="1295161776223959221">"Slika sa desertima"</string>
<string name="start_dreams" msgid="5640361424498338327">"Čuvar ekrana"</string>
@@ -319,6 +321,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Ograničenje <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Upozorenje <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Poslovni režim"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Noćno svjetlo"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Noćno svjetlo je uključeno, dodirnite da isključite"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Noćno svjetlo je isključeno, dodirnite da uključite"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Nema nedavnih stavki"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Sve ste obrisali"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacije o aplikaciji"</string>
@@ -332,6 +337,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podjela po horizontali"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Podjela po vertikali"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Prilagođena podjela"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Napunjeno"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Punjenje"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Do kraja punjenja preostalo <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
@@ -346,8 +353,7 @@
<string name="zen_silence_introduction" msgid="3137882381093271568">"Ovim se blokiraju SVI zvukovi i vibracije, uključujući alarme, muziku, video zapise i igre."</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Prikaži manje važna obavještenja ispod"</string>
- <!-- no translation found for notification_tap_again (7590196980943943842) -->
- <skip />
+ <string name="notification_tap_again" msgid="7590196980943943842">"Dodirnite ponovo da otvorite"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Prevucite prema gore da otključate"</string>
<string name="phone_hint" msgid="4872890986869209950">"Prevucite preko ikone da otvorite telefon"</string>
<string name="voice_hint" msgid="8939888732119726665">"Prevucite preko ikone za glasovnu pomoć"</string>
@@ -437,6 +443,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> je dijaloški okvir za jačinu zvuka"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Dodirnite za povrat originala."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Koristite svoj profil za posao"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Pozovi"</item>
+ <item msgid="5997713001067658559">"Sistem"</item>
+ <item msgid="7858983209929864160">"Zazvoni"</item>
+ <item msgid="1850038478268896762">"Mediji"</item>
+ <item msgid="8265110906352372092">"Alarm"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<!-- String.format failed for translation -->
<!-- no translation found for volume_stream_content_description_unmute (4436631538779230857) -->
<skip />
@@ -509,21 +527,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Više postavki"</string>
<string name="notification_done" msgid="5279426047273930175">"Gotovo"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Kontrole <xliff:g id="APP_NAME">%1$s</xliff:g> obavještenja"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Boja i izgled"</string>
- <string name="night_mode" msgid="3540405868248625488">"Noćni način rada"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Kalibracija zaslona"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Uključeno"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Isključeno"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Automatsko uključivanje"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Prebaciti u Noćni način rada u skladu sa lokacijom i dobom dana"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Kada je Noćni režim rada uključen"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Koristiti tamne teme za OS Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Prilagođavanje nijanse"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Podešavanje osvijetljenosti"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Tamna tema se primjenjuje na ključna područja OS Android koja se obično prikazuju u svijetloj temi, kao što je meni Postavke."</string>
- <string name="color_apply" msgid="9212602012641034283">"Prihvati"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Potvrdi postavke"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"S nekim postavkama boja ovaj uređaj može biti neupotrebljiv. Kliknite U redu da biste potvrdili ove postavke boja ili sačekajte 10 sekundi da se postavke vrate na početnu vrijednost."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Upotreba baterije"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Ušteda baterije je isključena prilikom punjenja"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Ušteda baterije"</string>
@@ -542,7 +545,7 @@
<string name="keyboard_key_backspace" msgid="1559580097512385854">"Tipka za brisanje"</string>
<string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Pokreni/pauziraj"</string>
<string name="keyboard_key_media_stop" msgid="2859963958595908962">"Zaustavi"</string>
- <string name="keyboard_key_media_next" msgid="1894394911630345607">"Sljedeće"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Naprijed"</string>
<string name="keyboard_key_media_previous" msgid="4256072387192967261">"Prethodno"</string>
<string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Premotaj"</string>
<string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Ubrzaj"</string>
diff --git a/packages/SystemUI/res/values-ca/config.xml b/packages/SystemUI/res/values-ca/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-ca/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 789fdc3..3354f92 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3,5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Itinerància"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Vora"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Límit: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Advertiment: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Mode de feina"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Llum nocturna"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"La llum nocturna està encesa; toca aquí per apagar-la"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"La llum nocturna està apagada; toca aquí per encendre-la"</string>
<string name="recents_empty_message" msgid="808480104164008572">"No hi ha cap element recent"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Ho has esborrat tot"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informació de l\'aplicació"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisió horitzontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisió vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisió personalitzada"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carregada"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"S\'està carregant"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> per completar la càrrega"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> és el diàleg de volum"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Toca la notificació per restaurar el valor original."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Estàs utilitzant el perfil professional"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Truca"</item>
+ <item msgid="5997713001067658559">"Sistema"</item>
+ <item msgid="7858983209929864160">"Fes sonar"</item>
+ <item msgid="1850038478268896762">"Multimèdia"</item>
+ <item msgid="8265110906352372092">"Alarma"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Toca per activar el so."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Toca per activar la vibració. Pot ser que els serveis d\'accessibilitat se silenciïn."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Toca per silenciar el so. Pot ser que els serveis d\'accessibilitat se silenciïn."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Més opcions"</string>
<string name="notification_done" msgid="5279426047273930175">"Fet"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Controls de notificació de l\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Color i aparença"</string>
- <string name="night_mode" msgid="3540405868248625488">"Mode nocturn"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Calibra la pantalla"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Activat"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Desactivat"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Activa automàticament"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Canvia al mode nocturn d\'acord amb la ubicació i l\'hora del dia"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Quan el mode nocturn estigui activat"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Fes servir un tema fosc per a Android OS"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Ajusta el color"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Ajusta la brillantor"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"El tema fosc s\'aplica a les àrees clau d\'Android OS que normalment es mostren amb un tema clar, com ara Configuració."</string>
- <string name="color_apply" msgid="9212602012641034283">"Aplica"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Confirma la configuració"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Algunes opcions de configuració de color poden deixar el dispositiu inservible. Fes clic a D\'acord per confirmar la configuració de color; en cas contrari, la configuració es restablirà al cap de 10 segons."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Ús de la bateria"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"La funció Estalvi de bateria no està disponible durant la càrrega"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Estalvi de bateria"</string>
@@ -600,7 +604,7 @@
<string name="select_keycode" msgid="7413765103381924584">"Selecciona un botó de teclat"</string>
<string name="preview" msgid="9077832302472282938">"Previsualització"</string>
<string name="drag_to_add_tiles" msgid="7058945779098711293">"Arrossega per afegir funcions"</string>
- <string name="drag_to_remove_tiles" msgid="3361212377437088062">"Arrossega\'ls aquí per suprimir-los"</string>
+ <string name="drag_to_remove_tiles" msgid="3361212377437088062">"Arrossega aquí per suprimir una funció"</string>
<string name="qs_edit" msgid="2232596095725105230">"Edita"</string>
<string name="tuner_time" msgid="6572217313285536011">"Hora"</string>
<string-array name="clock_options">
diff --git a/packages/SystemUI/res/values-cs/config.xml b/packages/SystemUI/res/values-cs/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-cs/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 68ce2cb..d64e5d9 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -145,7 +145,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3,5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -323,6 +325,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Limit: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Upozornění při <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Pracovní režim"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Noční režim"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Noční režim je zapnut, klepnutím jej vypnete"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Noční režim je vypnut, klepnutím jej zapnete"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Žádné nedávné položky"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Vše je vymazáno"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informace o aplikaci"</string>
@@ -336,6 +341,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Vodorovné rozdělení"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikální rozdělení"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Vlastní rozdělení"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Nabito"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Nabíjení"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> do plného nabití"</string>
@@ -440,6 +447,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> je dialog hlasitosti"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Klepnutím obnovíte původní nastavení."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Používáte pracovní profil"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Hovor"</item>
+ <item msgid="5997713001067658559">"Systém"</item>
+ <item msgid="7858983209929864160">"Prozvonit"</item>
+ <item msgid="1850038478268896762">"Média"</item>
+ <item msgid="8265110906352372092">"Budík"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Klepnutím zapnete zvuk."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Klepnutím aktivujete režim vibrací. Služby přístupnosti mohou být ztlumeny."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Klepnutím vypnete zvuk. Služby přístupnosti mohou být ztlumeny."</string>
@@ -510,21 +529,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Další nastavení"</string>
<string name="notification_done" msgid="5279426047273930175">"Hotovo"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Nastavení oznámení aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Barva a vzhled"</string>
- <string name="night_mode" msgid="3540405868248625488">"Noční režim"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Kalibrovat displej"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Zapnuto"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Vypnuto"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Zapnout automaticky"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Přejít do nočního režimu automaticky na základě místa a denní doby"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Když je noční režim zapnutý"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Použít v systému Android tmavý motiv"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Upravit tónování"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Upravit jas"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"V hlavních oblastech systému Android, které jsou běžně zobrazovány ve světlém motivu (například Nastavení), se použije tmavý motiv."</string>
- <string name="color_apply" msgid="9212602012641034283">"Použít"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Ověření nastavení"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Některá nastavení barev mohou způsobit, že zařízení nebude použitelné. Kliknutím na OK toto nastavení barev potvrdíte, v opačném případě se nastavení po 10 sekundách resetuje."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Využití baterie"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Spořič baterie při nabíjení není k dispozici."</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Spořič baterie"</string>
diff --git a/packages/SystemUI/res/values-da/config.xml b/packages/SystemUI/res/values-da/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-da/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 603d1e0..da8daa4 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"Over 4G"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Grænse: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Advarsel ved <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Arbejdstilstand"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Nattelys"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Nattelys er tændt. Tryk for at slukke"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Nattelys er slukket. Tryk for at tænde"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Ingen nye elementer"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Du har ryddet alt"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Oplysninger om applikationen"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Opdel vandret"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Opdel lodret"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Opdel brugerdefineret"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Opladet"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Oplader"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> indtil fuld opladet"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> er dialogboksen for lydstyrke"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Tryk for at gendanne det oprindelige."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Du bruger din arbejdsprofil"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Opkald"</item>
+ <item msgid="5997713001067658559">"System"</item>
+ <item msgid="7858983209929864160">"Ring"</item>
+ <item msgid="1850038478268896762">"Medier"</item>
+ <item msgid="8265110906352372092">"Alarm"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Tryk for at slå lyden til."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tryk for at konfigurere til at vibrere. Tilgængelighedstjenester kan blive deaktiveret."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Tryk for at slå lyden fra. Lyden i tilgængelighedstjenester kan blive slået fra."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Flere indstillinger"</string>
<string name="notification_done" msgid="5279426047273930175">"Færdig"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Kontrolelementer til underretninger for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Farve og udseende"</string>
- <string name="night_mode" msgid="3540405868248625488">"Nattilstand"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Kalibrer skærmen"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Til"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Fra"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Slå automatisk til"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Skift til natfunktion alt efter stedet og tidspunktet"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Når natfunktion er slået til"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Brug mørkt tema til Android OS"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Juster farvetonen"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Juster lysstyrken"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Det mørke tema anvendes på centrale områder i Android OS, der normalt vises med lyst tema, f.eks. Indstillinger."</string>
- <string name="color_apply" msgid="9212602012641034283">"Anvend"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Bekræft indstillingerne"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Nogle farveindstillinger kan medføre, at du ikke kan bruge enheden. Klik på OK for at bekræfte disse farveindstillinger. Ellers nulstilles disse indstillinger efter ti sekunder."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Batteriforbrug"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Batterisparefunktionen er ikke tilgængelig under opladning"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Batterisparefunktion"</string>
diff --git a/packages/SystemUI/res/values-de/config.xml b/packages/SystemUI/res/values-de/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-de/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 47040db..46078d9 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3,5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -319,6 +321,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> Datenlimit"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Warnung für <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Arbeitsmodus"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Nachtlicht"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Nachtlicht an, zum Deaktivieren tippen"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Nachtlicht aus, zum Aktivieren tippen"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Keine kürzlich verwendeten Elemente"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Du hast alles gelöscht"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"App-Info"</string>
@@ -332,6 +337,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Geteilte Schaltfläche – horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Geteilte Schaltfläche – vertikal"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Geteilte Schaltfläche – benutzerdefiniert"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Aufgeladen"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Wird aufgeladen"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Voll in <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
@@ -436,6 +443,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> regelt die Lautstärke."</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Tippe, um das Original wiederherzustellen."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Du verwendest dein Arbeitsprofil."</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Anruf"</item>
+ <item msgid="5997713001067658559">"System"</item>
+ <item msgid="7858983209929864160">"Klingeln lassen"</item>
+ <item msgid="1850038478268896762">"Medien"</item>
+ <item msgid="8265110906352372092">"Wecker"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Zum Aufheben der Stummschaltung tippen."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tippen, um Vibrieren festzulegen. Bedienungshilfen werden unter Umständen stummgeschaltet."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Zum Stummschalten tippen. Bedienungshilfen werden unter Umständen stummgeschaltet."</string>
@@ -506,21 +525,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Weitere Einstellungen"</string>
<string name="notification_done" msgid="5279426047273930175">"Fertig"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g>-Benachrichtigungseinstellungen"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Farbe und Darstellung"</string>
- <string name="night_mode" msgid="3540405868248625488">"Nachtmodus"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Bildschirm kalibrieren"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"An"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Aus"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Automatisch aktivieren"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Abhängig von Standort und Tageszeit in den Nachtmodus wechseln"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Bei aktiviertem Nachtmodus"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Dunkles Design unter Android OS verwenden"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Farbton anpassen"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Helligkeit anpassen"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Das dunkle Design wird unter Android OS in allen Hauptbereichen übernommen, die normalerweise hell dargestellt werden, wie beispielsweise Einstellungen."</string>
- <string name="color_apply" msgid="9212602012641034283">"Übernehmen"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Einstellungen bestätigen"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Einige Farbeinstellungen können dazu führen, dass das Gerät nicht mehr genutzt werden kann. Klicke auf \"OK\", um diese Farbeinstellungen zu bestätigen. Anderenfalls werden diese Einstellungen in 10 Sekunden zurückgesetzt."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Akkunutzung"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Der Energiesparmodus ist beim Aufladen nicht verfügbar."</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Energiesparmodus"</string>
diff --git a/packages/SystemUI/res/values-el/config.xml b/packages/SystemUI/res/values-el/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-el/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 0c3c6ff..0a327ab 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Περιαγωγή"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Όριο <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Προειδοποίηση για <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Λειτουργία εργασίας"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Νυχτερινός φωτισμός"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Ο Νυχτερινός φωτισμός είναι ενεργοποιημένος. Πατήστε για απενεργοποίηση."</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Ο Νυχτερινός φωτισμός είναι απενεργοποιημένος. Πατήστε για ενεργοποίηση."</string>
<string name="recents_empty_message" msgid="808480104164008572">"Δεν υπάρχουν πρόσφατα στοιχεία"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Έχει γίνει εκκαθάριση όλων των στοιχείων"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Πληροφορίες εφαρμογής"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Οριζόντιος διαχωρισμός"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Κάθετος διαχωρισμός"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Προσαρμοσμένος διαχωρισμός"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Φορτίστηκε"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Φόρτιση"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> για πλήρη φόρτιση"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> αποτελεί το παράθυρο διαλόγου ελέγχου έντασης"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Πατήστε για να επαναφέρετε την αρχική μορφή της εικόνας."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Χρησιμοποιείτε το προφίλ εργασίας σας"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Κλήση"</item>
+ <item msgid="5997713001067658559">"Σύστημα"</item>
+ <item msgid="7858983209929864160">"Ήχος κλήσης"</item>
+ <item msgid="1850038478268896762">"Μέσα"</item>
+ <item msgid="8265110906352372092">"Ξυπνητήρι"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Πατήστε για κατάργηση σίγασης."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Πατήστε για ενεργοποιήσετε τη δόνηση. Οι υπηρεσίες προσβασιμότητας ενδέχεται να τεθούν σε σίγαση."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Πατήστε για σίγαση. Οι υπηρεσίες προσβασιμότητας ενδέχεται να τεθούν σε σίγαση."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Περισσότερες ρυθμίσεις"</string>
<string name="notification_done" msgid="5279426047273930175">"Τέλος"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Στοιχεία ελέγχου κοινοποίησης <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Χρώμα και εμφάνιση"</string>
- <string name="night_mode" msgid="3540405868248625488">"Νυχτερινή λειτουργία"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Βαθμονόμηση οθόνης"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Ενεργή"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Ανενεργή"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Αυτόματη ενεργοποίηση"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Αλλαγή σε νυχτερινή λειτουργία όπως απαιτείται βάσει τοποθεσίας και ώρας της ημέρας"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Όταν είναι ενεργή η νυχτερινή λειτουργία"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Χρήση σκοτεινού θέματος για Android OS"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Ρύθμιση απόχρωσης"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Ρύθμιση φωτεινότητας"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Το σκούρο θέμα εφαρμόζεται σε βασικές περιοχές του λειτουργικού συστήματος Android οι οποίες συνήθως εμφανίζονται με φωτεινό θέμα, όπως οι Ρυθμίσεις."</string>
- <string name="color_apply" msgid="9212602012641034283">"Εφαρμογή"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Επιβεβαίωση ρυθμίσεων"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Ορισμένες ρυθμίσεις χρωμάτων μπορεί να μην επιτρέπουν τη χρήση αυτής της συσκευής. Κάντε κλικ στην επιλογή OK για να επιβεβαιώσετε αυτές τις ρυθμίσεις χρωμάτων, διαφορετικά θα γίνει επαναφορά αυτών των ρυθμίσεων μετά από 10 δευτερόλεπτα."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Χρήση της μπαταρίας"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Η εξοικονόμηση μπαταρίας δεν είναι διαθέσιμη κατά τη διάρκεια της φόρτισης"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Εξοικονόμηση μπαταρίας"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/config.xml b/packages/SystemUI/res/values-en-rAU/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-en-rAU/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 2ede279..9443ad9 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> limit"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> warning"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Work mode"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Night Light"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Night Light on, tap to turn off"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Night Light off, tap to turn on"</string>
<string name="recents_empty_message" msgid="808480104164008572">"No recent items"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"You\'ve cleared everything"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Application Info"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Split Vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Split Customised"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Charged"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Charging"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> until full"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> is the volume dialogue"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Tap to restore the original."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"You\'re using your work profile"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Call"</item>
+ <item msgid="5997713001067658559">"System"</item>
+ <item msgid="7858983209929864160">"Ring"</item>
+ <item msgid="1850038478268896762">"Media"</item>
+ <item msgid="8265110906352372092">"Alarm"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Tap to unmute."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Tap to mute. Accessibility services may be muted."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"More settings"</string>
<string name="notification_done" msgid="5279426047273930175">"Done"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> notification controls"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Colour and appearance"</string>
- <string name="night_mode" msgid="3540405868248625488">"Night mode"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Calibrate display"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"On"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Off"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Turn on automatically"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Switch into Night Mode as appropriate for location and time of day"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"When Night Mode is on"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Use dark theme for Android OS"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Adjust tint"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Adjust brightness"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"The dark theme is applied to core areas of Android OS that are normally displayed in a light theme, such as Settings."</string>
- <string name="color_apply" msgid="9212602012641034283">"Apply"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Confirm Settings"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Some colour settings can make this device unusable. Click OK to confirm these colour settings, otherwise these settings will reset after 10 seconds."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Battery usage"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Battery Saver not available during charging"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Battery Saver"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/config.xml b/packages/SystemUI/res/values-en-rGB/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-en-rGB/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 2ede279..9443ad9 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> limit"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> warning"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Work mode"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Night Light"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Night Light on, tap to turn off"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Night Light off, tap to turn on"</string>
<string name="recents_empty_message" msgid="808480104164008572">"No recent items"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"You\'ve cleared everything"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Application Info"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Split Vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Split Customised"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Charged"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Charging"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> until full"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> is the volume dialogue"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Tap to restore the original."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"You\'re using your work profile"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Call"</item>
+ <item msgid="5997713001067658559">"System"</item>
+ <item msgid="7858983209929864160">"Ring"</item>
+ <item msgid="1850038478268896762">"Media"</item>
+ <item msgid="8265110906352372092">"Alarm"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Tap to unmute."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Tap to mute. Accessibility services may be muted."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"More settings"</string>
<string name="notification_done" msgid="5279426047273930175">"Done"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> notification controls"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Colour and appearance"</string>
- <string name="night_mode" msgid="3540405868248625488">"Night mode"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Calibrate display"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"On"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Off"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Turn on automatically"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Switch into Night Mode as appropriate for location and time of day"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"When Night Mode is on"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Use dark theme for Android OS"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Adjust tint"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Adjust brightness"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"The dark theme is applied to core areas of Android OS that are normally displayed in a light theme, such as Settings."</string>
- <string name="color_apply" msgid="9212602012641034283">"Apply"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Confirm Settings"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Some colour settings can make this device unusable. Click OK to confirm these colour settings, otherwise these settings will reset after 10 seconds."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Battery usage"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Battery Saver not available during charging"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Battery Saver"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/config.xml b/packages/SystemUI/res/values-en-rIN/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-en-rIN/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 2ede279..9443ad9 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> limit"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> warning"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Work mode"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Night Light"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Night Light on, tap to turn off"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Night Light off, tap to turn on"</string>
<string name="recents_empty_message" msgid="808480104164008572">"No recent items"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"You\'ve cleared everything"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Application Info"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Split Vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Split Customised"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Charged"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Charging"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> until full"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> is the volume dialogue"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Tap to restore the original."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"You\'re using your work profile"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Call"</item>
+ <item msgid="5997713001067658559">"System"</item>
+ <item msgid="7858983209929864160">"Ring"</item>
+ <item msgid="1850038478268896762">"Media"</item>
+ <item msgid="8265110906352372092">"Alarm"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Tap to unmute."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Tap to mute. Accessibility services may be muted."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"More settings"</string>
<string name="notification_done" msgid="5279426047273930175">"Done"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> notification controls"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Colour and appearance"</string>
- <string name="night_mode" msgid="3540405868248625488">"Night mode"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Calibrate display"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"On"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Off"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Turn on automatically"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Switch into Night Mode as appropriate for location and time of day"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"When Night Mode is on"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Use dark theme for Android OS"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Adjust tint"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Adjust brightness"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"The dark theme is applied to core areas of Android OS that are normally displayed in a light theme, such as Settings."</string>
- <string name="color_apply" msgid="9212602012641034283">"Apply"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Confirm Settings"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Some colour settings can make this device unusable. Click OK to confirm these colour settings, otherwise these settings will reset after 10 seconds."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Battery usage"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Battery Saver not available during charging"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Battery Saver"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/config.xml b/packages/SystemUI/res/values-es-rUS/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-es-rUS/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index cd08f34..602d6b2 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3,5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -319,6 +321,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Límite de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Advertencia de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Modo de trabajo"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Luz nocturna"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Presiona para desactivar la Luz nocturna"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Presiona para activar la Luz nocturna"</string>
<string name="recents_empty_message" msgid="808480104164008572">"No hay elementos recientes"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Todo borrado"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Información de la aplicación"</string>
@@ -332,6 +337,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"División horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"División vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"División personalizada"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Cargada"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Cargando"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> para completarse"</string>
@@ -436,6 +443,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> es el cuadro de diálogo de volumen."</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Presiona para restablecer el original."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Estás usando tu perfil de trabajo"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Llamar"</item>
+ <item msgid="5997713001067658559">"Sistema"</item>
+ <item msgid="7858983209929864160">"Hacer sonar"</item>
+ <item msgid="1850038478268896762">"Multimedia"</item>
+ <item msgid="8265110906352372092">"Alarma"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Presiona para dejar de silenciar."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Presiona para establecer el modo vibración. Es posible que los servicios de accesibilidad estén silenciados."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Presiona para silenciar. Es posible que los servicios de accesibilidad estén silenciados."</string>
@@ -506,21 +525,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Más opciones de configuración"</string>
<string name="notification_done" msgid="5279426047273930175">"Listo"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Controles de notificaciones de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Color y apariencia"</string>
- <string name="night_mode" msgid="3540405868248625488">"Modo nocturno"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Calibrar pantalla"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Activado"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Desactivado"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Activar automáticamente"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Cambiar a modo nocturno según corresponda en relación con la ubicación y hora del día"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Cuando el modo nocturno está activado"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Usar tema oscuro para el SO Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Ajustar tinte"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Ajustar brillo"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"El tema oscuro se aplica en las áreas principales del SO Android que suelen mostrarse con un tema claro, como Configuración."</string>
- <string name="color_apply" msgid="9212602012641034283">"Aplicar"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Confirmar la configuración"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Algunas opciones de configuración de color pueden provocar que el dispositivo quede inutilizable. Haz clic en Aceptar para confirmar estos parámetros de color. De lo contrario, la configuración se restablecerá en diez segundos."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Uso de la batería"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Ahorro de batería no está disponible durante la carga"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Ahorro de batería"</string>
diff --git a/packages/SystemUI/res/values-es/config.xml b/packages/SystemUI/res/values-es/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-es/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 5e0fa43e..ba11d6f 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5 G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Itinerancia"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Tipo Edge"</string>
@@ -319,6 +321,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Límite de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Advertencia de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Modo de trabajo"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Luz nocturna"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Luz nocturna activada; toca aquí para desactivarla"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Luz nocturna desactivada; toca aquí para activarla"</string>
<string name="recents_empty_message" msgid="808480104164008572">"No hay elementos recientes"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Has rechazado todo"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Información de la aplicación"</string>
@@ -332,6 +337,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"División horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"División vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"División personalizada"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Cargada"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Cargando"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> para completarse"</string>
@@ -436,6 +443,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> es el cuadro de diálogo de volumen"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Toca para restaurar el original."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Estás usando tu perfil de trabajo"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Llamar"</item>
+ <item msgid="5997713001067658559">"Sistema"</item>
+ <item msgid="7858983209929864160">"Hacer sonar"</item>
+ <item msgid="1850038478268896762">"Multimedia"</item>
+ <item msgid="8265110906352372092">"Alarma"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Toca para activar el sonido."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Toca para poner el dispositivo en vibración. Los servicios de accesibilidad pueden silenciarse."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Toca para silenciar. Los servicios de accesibilidad pueden silenciarse."</string>
@@ -506,21 +525,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Más ajustes"</string>
<string name="notification_done" msgid="5279426047273930175">"Listo"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Controles de notificaciones de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Color y aspecto"</string>
- <string name="night_mode" msgid="3540405868248625488">"Modo nocturno"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Calibrar pantalla"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Sí"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"No"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Activar automáticamente"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Cambiar al modo nocturno cuando proceda según la ubicación y la hora del día"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Cuando el modo nocturno esté activado"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Tema oscuro para sistema operativo Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Ajustar tono"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Ajustar brillo"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"El tema oscuro se aplica a las áreas principales del sistema operativo Android que normalmente se muestran con un tema claro, como la aplicación Ajustes."</string>
- <string name="color_apply" msgid="9212602012641034283">"Aplicar"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Confirmar configuración"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Algunas opciones de configuración de color pueden hacer que el dispositivo no se pueda utilizar. Haz clic en Aceptar para confirmar esta configuración. Si no lo haces, se restablecerá esta configuración cuando transcurran 10 segundos."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Uso de la batería"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Ahorro de batería no disponible mientras se carga el dispositivo"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Ahorro de batería"</string>
diff --git a/packages/SystemUI/res/values-et-rEE/config.xml b/packages/SystemUI/res/values-et-rEE/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-et-rEE/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index 405d351..bfc55e9 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3,5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Rändlus"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Serv"</string>
@@ -319,6 +321,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Limiit: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> hoiatus"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Töörežiim"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Öövalgus"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Öövalgus on sees, puudutage väljalülitamiseks"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Öövalgus on väljas, puudutage sisselülitamiseks"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Hiljutisi üksusi pole"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Olete kõik ära kustutanud"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Rakenduste teave"</string>
@@ -332,6 +337,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Horisontaalne poolitamine"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikaalne poolitamine"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Kohandatud poolitamine"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Laetud"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Laadimine"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Täislaadimiseks kulub <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
@@ -436,6 +443,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> on helitugevuse dialoog"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Puudutage originaali taastamiseks."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Kasutate oma tööprofiili"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Helistamine"</item>
+ <item msgid="5997713001067658559">"Süsteem"</item>
+ <item msgid="7858983209929864160">"Helin"</item>
+ <item msgid="1850038478268896762">"Meedia"</item>
+ <item msgid="8265110906352372092">"Äratus"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Puudutage vaigistuse tühistamiseks."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Puudutage värinarežiimi määramiseks. Juurdepääsetavuse teenused võidakse vaigistada."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Puudutage vaigistamiseks. Juurdepääsetavuse teenused võidakse vaigistada."</string>
@@ -506,21 +525,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Rohkem seadeid"</string>
<string name="notification_done" msgid="5279426047273930175">"Valmis"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> märguannete juhtnupud"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Värv ja ilme"</string>
- <string name="night_mode" msgid="3540405868248625488">"Öörežiim"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Ekraani kalibreerimine"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Sees"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Väljas"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Lülita automaatselt sisse"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Lülita öörežiimile, kui see on asukoha ja kellaaja suhtes sobilik"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Kui öörežiim on sees"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Kasuta Android OS-is tumedat teemat"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Reguleeri tooni"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Reguleeri heledust"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Tume teema rakendatakse Android OS-i põhialadele, mis kuvatakse tavaliselt heleda teemaga (nt seaded)."</string>
- <string name="color_apply" msgid="9212602012641034283">"Rakenda"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Seadete kinnitamine"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Mõni värviseade ei saa seadet võib-olla kasutada. Nende värviseadete kinnitamiseks klõpsake OK, muidu lähtestatakse need seaded 10 sekundi pärast."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Akukasutus"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Akusäästja pole laadimise ajal saadaval"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Akusäästja"</string>
diff --git a/packages/SystemUI/res/values-eu-rES/config.xml b/packages/SystemUI/res/values-eu-rES/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-eu-rES/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml
index d8e3446..a340b3d 100644
--- a/packages/SystemUI/res/values-eu-rES/strings.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Ibiltaritza"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"EDGE"</string>
@@ -319,6 +321,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Muga: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Abisua: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Lan modua"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Gaueko argia"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Aktibatuta dago gaueko argia. Sakatu desaktibatzeko."</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Desaktibatuta dago gaueko argia. Sakatu aktibatzeko."</string>
<string name="recents_empty_message" msgid="808480104164008572">"Ez dago azkenaldi honetako ezer"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Dena garbitu duzu"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Aplikazioaren informazioa"</string>
@@ -332,6 +337,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Zatitze horizontala"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Zatitze bertikala"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Zatitze pertsonalizatua"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Kargatuta"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Kargatzen"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> falta zaizkio guztiz kargatzeko"</string>
@@ -436,6 +443,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> da bolumenaren leihoa"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Sakatu jatorrizkora leheneratzeko."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Work profila erabiltzen ari zara"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Deitu"</item>
+ <item msgid="5997713001067658559">"Sistema"</item>
+ <item msgid="7858983209929864160">"Tonua"</item>
+ <item msgid="1850038478268896762">"Multimedia-edukia"</item>
+ <item msgid="8265110906352372092">"Alarma"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth konexioa"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Sakatu audioa aktibatzeko."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Sakatu dardara ezartzeko. Baliteke erabilerraztasun-eginbideen audioa desaktibatzea."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Sakatu audioa desaktibatzeko. Baliteke erabilerraztasun-eginbideen audioa desaktibatzea."</string>
@@ -506,21 +525,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Ezarpen gehiago"</string>
<string name="notification_done" msgid="5279426047273930175">"Eginda"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren jakinarazpenak kontrolatzeko aukerak"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Kolorea eta itxura"</string>
- <string name="night_mode" msgid="3540405868248625488">"Gau modua"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Kalibratu pantaila"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Aktibatuta"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Desaktibatuta"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Aktibatu automatikoki"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Aldatu Gau modura, kokapena eta ordua kontuan izanda"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Gau modua aktibatuta dagoenean"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Erabili gai iluna Android sistema eragilean"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Doitu kolorea"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Doitu distira"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Gai iluna Android sistema eragileko eremu nagusietan aplikatzen da. Normalean gai argian bistaratzen dira eremu horiek, adibidez, Ezarpenak atalean."</string>
- <string name="color_apply" msgid="9212602012641034283">"Aplikatu"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Berretsi ezarpenak"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Baliteke gailua kolore-ezarpen batzuekin ezin erabili izatea. Kolore-ezarpenak berresteko, sakatu Ados. Bestela, hamar segundoren buruan berrezarriko dira ezarpenak."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Bateriaren erabilera"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Bateria-aurrezlea ez dago erabilgarri gailua kargatzen ari denean"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Bateria-aurrezlea"</string>
diff --git a/packages/SystemUI/res/values-fa/config.xml b/packages/SystemUI/res/values-fa/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-fa/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 7c77d59..58a5d93 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"رومینگ"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> محدودیت"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"هشدار <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"حالت کار"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"نور شب"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"نور شب روشن است، برای خاموشکردن آن ضربه بزنید"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"نور شب خاموش است، برای روشنشدن آن ضربه بزنید"</string>
<string name="recents_empty_message" msgid="808480104164008572">"بدون موارد اخیر"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"همهچیز را پاک کردهاید"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"اطلاعات برنامه"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"تقسیم افقی"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"تقسیم عمودی"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"سفارشی کردن تقسیم"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"شارژ کامل شد"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"در حال شارژ شدن"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> مانده تا شارژ کامل شود"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> کنترلکننده صدا است"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"برای بازیابی نسخه اصلی ضربه بزنید."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"درحال استفاده از نمایه کاریتان هستید"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"تماس"</item>
+ <item msgid="5997713001067658559">"سیستم"</item>
+ <item msgid="7858983209929864160">"تماس"</item>
+ <item msgid="1850038478268896762">"رسانه"</item>
+ <item msgid="8265110906352372092">"هشدار"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"بلوتوث"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. برای باصدا کردن ضربه بزنید."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. برای تنظیم روی لرزش ضربه بزنید. ممکن است سرویسهای دسترسپذیری بیصدا شوند."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. برای بیصدا کردن ضربه بزنید. ممکن است سرویسهای دسترسپذیری بیصدا شوند."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"تنظیمات بیشتر"</string>
<string name="notification_done" msgid="5279426047273930175">"تمام"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"کنترلهای اعلان <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"رنگ و ظاهر"</string>
- <string name="night_mode" msgid="3540405868248625488">"حالت شب"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"درجهبندی نمایشگر"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"روشن"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"خاموش"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"روشن شدن خودکار"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"تغییر به حالت شب وقتی برای مکان و زمان روز مناسب است"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"وقتی حالت شب روشن است"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"استفاده از زمینه تیره برای سیستمعامل Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"تنظیم سایهرنگ"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"تنظیم روشنایی"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"زمینه تیره بر قسمتهای اصلی سیستمعامل Android که بهطور معمول با زمینه روشن نشان داده میشوند (مثل «تنظیمات») اعمال میشود."</string>
- <string name="color_apply" msgid="9212602012641034283">"اعمال کردن"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"تأیید تنظیمات"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"بعضی از تنظیمات رنگ میتوانند این دستگاه را غیرقابل استفاده کنند. برای تأیید این تنظیمات رنگ روی «تأیید» کلیک کنید، در غیر این صورت این تغییرات بعد از ۱۰ ثانیه بازنشانی میشوند."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"مصرف باتری"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"هنگام شارژ شدن، «بهینهسازی باتری» در دسترس نیست"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"بهینهسازی باتری"</string>
diff --git a/packages/SystemUI/res/values-fi/config.xml b/packages/SystemUI/res/values-fi/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-fi/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 3ae25e1..5ff50be 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3,5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"kiintiö <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> – varoitus"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Työtila"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Yövalo"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Yövalo on käytössä. Poista se käytöstä koskettamalla."</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Yövalo ei ole käytössä. Ota se käyttöön koskettamalla."</string>
<string name="recents_empty_message" msgid="808480104164008572">"Ei viimeaikaisia kohteita"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Kaikki on hoidettu."</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Sovellustiedot"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Vaakasuuntainen jako"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Pystysuuntainen jako"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Muokattu jako"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Ladattu"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Ladataan"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> kunnes täynnä"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> on äänenvoimakkuusvalinta."</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Palauta alkuperäinen napauttamalla."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Käytät työprofiilia."</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Soita"</item>
+ <item msgid="5997713001067658559">"Järjestelmä"</item>
+ <item msgid="7858983209929864160">"Soittoääni"</item>
+ <item msgid="1850038478268896762">"Media"</item>
+ <item msgid="8265110906352372092">"Herätys"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Poista mykistys koskettamalla."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Siirry värinätilaan koskettamalla. Myös esteettömyyspalvelut saattavat mykistyä."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Mykistä koskettamalla. Myös esteettömyyspalvelut saattavat mykistyä."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Lisäasetukset"</string>
<string name="notification_done" msgid="5279426047273930175">"Valmis"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ilmoitusten hallinta"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Väri ja ulkoasu"</string>
- <string name="night_mode" msgid="3540405868248625488">"Yötila"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Kalibroi näyttö"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Käytössä"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Pois käytöstä"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Ota käyttöön automaattisesti"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Ota yötila käyttöön sijainnin ja kellonajan perusteella."</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Kun yötila on käytössä"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Käytä tummaa teemaa käyttöjärjestelmässä"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Säädä sävytystä"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Säädä kirkkautta"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Tumma teema tulee käyttöön Android-käyttöjärjestelmän ydinosissa, kuten Asetuksissa, joissa käytetään tavallisesti vaaleaa teemaa."</string>
- <string name="color_apply" msgid="9212602012641034283">"Käytä"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Vahvista asetukset"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Jotkin väriasetukset voivat häiritä laitteen käyttöä. Vahvista uudet väriasetukset valitsemalla OK. Muussa tapauksessa aiemmat asetukset palautetaan 10 sekunnin kuluttua."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Akun käyttö"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Virransäästö ei ole käytettävissä latauksen aikana."</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Virransäästö"</string>
@@ -599,7 +603,7 @@
<string name="keycode_description" msgid="1403795192716828949">"Näppäinkoodi-painikkeet sallivat näppäimistön näppäimien lisäämisen navigointipalkkiin. Kun painiketta painetaan, se jäljittelee valittua näppäintä. Valitse ensin painikkeen kohteena oleva näppäin, sitten painikkeessa näkyvä kuva."</string>
<string name="select_keycode" msgid="7413765103381924584">"Valitse näppäimistön näppäin"</string>
<string name="preview" msgid="9077832302472282938">"Esikatselu"</string>
- <string name="drag_to_add_tiles" msgid="7058945779098711293">"Lisää osioita vetämällä."</string>
+ <string name="drag_to_add_tiles" msgid="7058945779098711293">"Lisää osioita vetämällä"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Poista vetämällä tähän."</string>
<string name="qs_edit" msgid="2232596095725105230">"Muokkaa"</string>
<string name="tuner_time" msgid="6572217313285536011">"Aika"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/config.xml b/packages/SystemUI/res/values-fr-rCA/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-fr-rCA/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 9aa176f..7d50fee 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3G+"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Itinérance"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"EDGE"</string>
@@ -319,6 +321,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Limite : <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Avertissement : <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Mode Travail"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Éclairage nocturne"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Le mode Éclairage nocturne est activé. Touchez ici pour le désactiver."</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Le mode Éclairage nocturne est désactivé. Touchez ici pour l\'activer."</string>
<string name="recents_empty_message" msgid="808480104164008572">"Aucun élément récent"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Vous avez tout effacé"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Détails de l\'application"</string>
@@ -332,6 +337,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Séparation horizontale"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Séparation verticale"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Séparation personnalisée"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Chargée"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Charge en cours..."</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Chargée dans <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
@@ -436,6 +443,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> correspond à la boîte de dialogue du volume"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Touchez pour restaurer l\'original."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Vous utilisez votre profil professionnel."</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Appeler"</item>
+ <item msgid="5997713001067658559">"Système"</item>
+ <item msgid="7858983209929864160">"Sonnerie"</item>
+ <item msgid="1850038478268896762">"Multimédia"</item>
+ <item msgid="8265110906352372092">"Alarme"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Touchez pour réactiver le son."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Touchez pour activer les vibrations. Il est possible de couper le son des services d\'accessibilité."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Touchez pour couper le son. Il est possible de couper le son des services d\'accessibilité."</string>
@@ -506,21 +525,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Plus de paramètres"</string>
<string name="notification_done" msgid="5279426047273930175">"Terminé"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Commandes de notification pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Couleur et apparence"</string>
- <string name="night_mode" msgid="3540405868248625488">"Mode Nuit"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Calibrer l\'affichage"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Activé"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Désactivé"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Activer automatiquement"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Passer en mode Nuit en fonction de la position et de l\'heure de la journée"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Lorsque le mode Nuit est activé"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Utiliser thème foncé pour Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Ajuster la coloration"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Régler la luminosité"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Le thème foncé est appliqué à des zones essentielles de la plateforme Android qui sont habituellement affichées dans un thème clair, comme les paramètres."</string>
- <string name="color_apply" msgid="9212602012641034283">"Appliquer"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Confirmer les paramètres"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Certains paramètres de couleurs peuvent rendre cet appareil inutilisable. Cliquez sur « OK » pour valider ces paramètres, sinon ils seront réinitialisés après 10 secondes."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Utilisation de la pile"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Le mode Économie d\'énergie n\'est pas accessible pendant la charge"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Économie d\'énergie"</string>
diff --git a/packages/SystemUI/res/values-fr/config.xml b/packages/SystemUI/res/values-fr/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-fr/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index b6c08ca..447255b 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3G+"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Itinérance"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"EDGE"</string>
@@ -312,13 +314,16 @@
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lampe de poche"</string>
<string name="quick_settings_cellular_detail_title" msgid="8575062783675171695">"Données mobiles"</string>
- <string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Consommation des données"</string>
+ <string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Conso des données"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Données restantes"</string>
<string name="quick_settings_cellular_detail_over_limit" msgid="967669665390990427">"Limite dépassée"</string>
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> utilisés"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> au maximum"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Avertissement : <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Mode Travail"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Éclairage nocturne"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Éclairage nocturne activé, appuyer pour désactiver"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Éclairage nocturne désactivé, appuyer pour activer"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Aucun élément récent"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Vous avez tout effacé."</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Infos application"</string>
@@ -332,6 +337,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Séparation horizontale"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Séparation verticale"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Séparation personnalisée"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Chargé"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"En charge"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Chargé dans <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
@@ -436,6 +443,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> correspond à la boîte de dialogue du volume"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Appuyez pour rétablir la version d\'origine."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Vous utilisez votre profil professionnel."</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Appeler"</item>
+ <item msgid="5997713001067658559">"Système"</item>
+ <item msgid="7858983209929864160">"Faire sonner"</item>
+ <item msgid="1850038478268896762">"Multimédia"</item>
+ <item msgid="8265110906352372092">"Alarme"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Appuyez pour ne plus ignorer."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Appuyez pour mettre en mode vibreur. Vous pouvez ignorer les services d\'accessibilité."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Appuyez pour ignorer. Vous pouvez ignorer les services d\'accessibilité."</string>
@@ -506,21 +525,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Plus de paramètres"</string>
<string name="notification_done" msgid="5279426047273930175">"Terminé"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Commandes de notification de l\'application <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Couleur et apparence"</string>
- <string name="night_mode" msgid="3540405868248625488">"Mode Nuit"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Calibrer l\'affichage"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Activé"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Désactivé"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Activer automatiquement"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Passer en mode Nuit en fonction de la position et de l\'heure de la journée"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Lorsque le mode Nuit est activé"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Utiliser thème foncé pour plate-forme Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Ajuster la coloration"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Régler la luminosité"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Le thème foncé est appliqué à des zones essentielles de la plate-forme Android qui sont habituellement affichées dans un thème clair, telles que les paramètres."</string>
- <string name="color_apply" msgid="9212602012641034283">"Appliquer"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Vérifier les paramètres"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Certains paramètres de couleurs peuvent rendre cet appareil inutilisable. Cliquez sur \"OK\" pour valider ces paramètres, sans quoi ils seront réinitialisés après 10 secondes."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Utilisation batterie"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"L\'économiseur de batterie n\'est pas disponible lorsque l\'appareil est en charge."</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Économiseur de batterie"</string>
diff --git a/packages/SystemUI/res/values-gl-rES/config.xml b/packages/SystemUI/res/values-gl-rES/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-gl-rES/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index c18bae7..6d0e366 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Itinerancia"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -319,6 +321,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Límite de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Advertencia <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Modo de traballo"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Luz nocturna"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"A función Luz nocturna está activada. Toca para desactivala"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"A función Luz nocturna está desactivada. Toca para activala"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Non hai elementos recentes"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Borraches todo"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Información da aplicación"</string>
@@ -332,6 +337,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Dividir en horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Dividir en vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Dividir de xeito personalizado"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Cargada"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Cargando"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> para completar a carga"</string>
@@ -436,6 +443,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> é o cadro de diálogo de volume"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Toca para restaurar o orixinal."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Estás usando o perfil de traballo"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Chamar"</item>
+ <item msgid="5997713001067658559">"Sistema"</item>
+ <item msgid="7858983209929864160">"Facer soar"</item>
+ <item msgid="1850038478268896762">"Multimedia"</item>
+ <item msgid="8265110906352372092">"Alarma"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Toca para activar o son."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Toca para establecer a vibración. Pódense silenciar os servizos de accesibilidade."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Toca para silenciar. Pódense silenciar os servizos de accesibilidade."</string>
@@ -506,21 +525,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Máis opcións"</string>
<string name="notification_done" msgid="5279426047273930175">"Feito"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Controis de notificacións de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Cor e aspecto"</string>
- <string name="night_mode" msgid="3540405868248625488">"Modo nocturno"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Calibrar pantalla"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Activado"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Desactivado"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Activar automaticamente"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Cambia ao modo nocturno segundo proceda para a localización e a hora do día"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Cando o modo nocturno está activado"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Usar tema escuro para SO Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Axustar ton"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Axustar brillo"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"O tema escuro aplícase ás áreas principais do SO Android que se mostran normalmente nun tema claro, como a configuración."</string>
- <string name="color_apply" msgid="9212602012641034283">"Aplicar"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Confirmar configuración"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Algunhas opcións de configuración de cor poden facer que este dispositivo sexa inutilizable. Fai clic en Aceptar para confirmar esta configuración de cor; en caso contrario, a configuración restablecerase tras 10 segundos."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Uso de batería"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"A función aforro de batería non está dispoñible durante a carga"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Aforro de batería"</string>
diff --git a/packages/SystemUI/res/values-gu-rIN/config.xml b/packages/SystemUI/res/values-gu-rIN/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-gu-rIN/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-gu-rIN/strings.xml b/packages/SystemUI/res/values-gu-rIN/strings.xml
index 8c00ebd..0663b41 100644
--- a/packages/SystemUI/res/values-gu-rIN/strings.xml
+++ b/packages/SystemUI/res/values-gu-rIN/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"રોમિંગ"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> મર્યાદા"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ચેતવણી"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"કાર્ય મોડ"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"રાત્રિ પ્રકાશ"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"રાત્રિ પ્રકાશ ચાલુ છે, બંધ કરવા માટે ટપ કરો"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"રાત્રિ પ્રકાશ બંધ છે, ચાલુ કરવા માટે ટૅપ કરો"</string>
<string name="recents_empty_message" msgid="808480104164008572">"કોઇ તાજેતરની આઇટમ્સ નથી"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"તમે બધું સાફ કર્યું"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"ઍપ્લિકેશન માહિતી"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"આડું વિભક્ત કરો"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ઊભું વિભક્ત કરો"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"કસ્ટમ વિભક્ત કરો"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"ચાર્જ થઈ ગયું"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"ચાર્જ થઈ રહ્યું છે"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"પૂર્ણ થવામાં <xliff:g id="CHARGING_TIME">%s</xliff:g> બાકી"</string>
@@ -344,8 +351,7 @@
<string name="zen_silence_introduction" msgid="3137882381093271568">"એલાર્મ્સ, સંગીત, વિડિઓઝ અને રમતો સહિત તમામ ધ્વનિઓ અને વાઇબ્રેશન્સને આ અવરોધિત કરે છે."</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"નીચે ઓછી તાકીદની સૂચનાઓ"</string>
- <!-- no translation found for notification_tap_again (7590196980943943842) -->
- <skip />
+ <string name="notification_tap_again" msgid="7590196980943943842">"ખોલવા માટે ફરીથી ટૅપ કરો"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"અનલૉક કરવા માટે ઉપર સ્વાઇપ કરો"</string>
<string name="phone_hint" msgid="4872890986869209950">"ફોન માટે આયકનમાંથી સ્વાઇપ કરો"</string>
<string name="voice_hint" msgid="8939888732119726665">"વૉઇસ સહાય માટે આયકનમાંથી સ્વાઇપ કરો"</string>
@@ -435,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> એ વૉલ્યૂમ સંવાદ છે"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"મૂળને પુનઃસ્થાપિત કરવા માટે ટૅપ કરો."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"તમે તમારી કાર્ય પ્રોફાઇલનો ઉપયોગ કરી રહ્યાં છો"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"કૉલ કરો"</item>
+ <item msgid="5997713001067658559">"સિસ્ટમ"</item>
+ <item msgid="7858983209929864160">"રિંગ કરો"</item>
+ <item msgid="1850038478268896762">"મીડિયા"</item>
+ <item msgid="8265110906352372092">"એલાર્મ"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. અનમ્યૂટ કરવા માટે ટૅપ કરો."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. વાઇબ્રેટ પર સેટ કરવા માટે ટૅપ કરો. ઍક્સેસિબિલિટી સેવાઓ મ્યૂટ કરવામાં આવી શકે છે."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. મ્યૂટ કરવા માટે ટૅપ કરો. ઍક્સેસિબિલિટી સેવાઓ મ્યૂટ કરવામાં આવી શકે છે."</string>
@@ -505,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"વધુ સેટિંગ્સ"</string>
<string name="notification_done" msgid="5279426047273930175">"થઈ ગયું"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> સૂચના નિયંત્રણો"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"રંગ અને દેખાવ"</string>
- <string name="night_mode" msgid="3540405868248625488">"રાત્રિ મોડ"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"પ્રદર્શન કૅલિબ્રેટ કરો"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"ચાલુ"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"બંધ"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"આપમેળે ચાલુ કરો"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"સ્થાન અને દિવસના સમય માટે યોગ્ય હોય તે રાત્રિ મોડ પર સ્વિચ કરો"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"જ્યારે રાત્રિ મોડ ચાલુ હોય"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android OS માટે ઘાટી થીમનો ઉપયોગ કરો"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"ટિંટ સમાયોજિત કરો"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"તેજ સમાયોજિત કરો"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"ઘાટી થીમને Android OS ના મુખ્ય ક્ષેત્રો પર લાગુ કરે છે જે સામાન્ય રીતે સેટિંગ્સ જેવી લાઇટ થીમમાં પ્રદર્શિત કરવામાં આવે છે."</string>
- <string name="color_apply" msgid="9212602012641034283">"લાગુ કરો"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"સેટિંગ્સની પુષ્ટિ કરો"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"કેટલીક રંગ સેટિંગ્સ આ ઉપકરણને બિનઉપયોગી બનાવી શકે છે. આ રંગ સેટિંગ્સની પુષ્ટિ કરવા માટે ઓકે ક્લિક કરો, અન્યથા 10 સેકંડ પછી આ સેટિંગ્સ ફરીથી સેટ થશે."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"બૅટરી વપરાશ"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ચાર્જિંગ દરમિયાન બૅટરી બચતકર્તા ઉપલબ્ધ નથી"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"બૅટરી બચતકર્તા"</string>
diff --git a/packages/SystemUI/res/values-hi/config.xml b/packages/SystemUI/res/values-hi/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-hi/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 983faed..2924556 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"रोमिंग"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"किनारा"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> सीमा"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> चेतावनी"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"कार्य मोड"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"नाइट लाइट"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"नाइट लाइट चालू है, बंद करने के लिए टैप करें"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"नाइट लाइट बंद है, चालू करने के लिए टैप करें"</string>
<string name="recents_empty_message" msgid="808480104164008572">"हाल ही का कोई आइटम नहीं"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"आपने सब कुछ साफ़ कर दिया है"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"एप्लिकेशन जानकारी"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"क्षैतिज रूप से विभाजित करें"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"लम्बवत रूप से विभाजित करें"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"कस्टम रूप से विभाजित करें"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"चार्ज हो गई है"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"चार्ज हो रही है"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"पूर्ण होने में <xliff:g id="CHARGING_TIME">%s</xliff:g> शेष"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> वॉल्यूम संवाद है"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"मूल को पुन: स्थापित करने के लिए टैप करें."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"आप अपनी कार्य प्रोफ़ाइल का उपयोग कर रहे हैं"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"कॉल करें"</item>
+ <item msgid="5997713001067658559">"सिस्टम"</item>
+ <item msgid="7858983209929864160">"रिंग करें"</item>
+ <item msgid="1850038478268896762">"मीडिया"</item>
+ <item msgid="8265110906352372092">"अलार्म"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"ब्लूटूथ"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. अनम्यूट करने के लिए टैप करें."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. कंपन पर सेट करने के लिए टैप करें. एक्सेस-योग्यता सेवाएं म्यूट हो सकती हैं."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. म्यूट करने के लिए टैप करें. एक्सेस-योग्यता सेवाएं म्यूट हो सकती हैं."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"और सेटिंग"</string>
<string name="notification_done" msgid="5279426047273930175">"हो गया"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> नोटिफ़िकेशन नियंत्रण"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"रंग और दिखावट"</string>
- <string name="night_mode" msgid="3540405868248625488">"रात्रि मोड"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"स्क्रीन को कैलिब्रेट करें"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"चालू"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"बंद"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"अपने आप चालू करें"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"स्थान और दिन के समय के लिए उपयुक्त रात्रि मोड में बदलें"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"रात्रि मोड के चालू होने पर"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android OS के लिए गहरी थीम का उपयोग करें"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"टिंट समायोजित करें"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"स्क्रीन की रोशनी समायोजित करें"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"गहरी थीम को Android OS के मुख्य क्षेत्रों पर लागू किया जाता है जिन्हें सामान्यतः सेटिंग जैसी हल्की थीम में प्रदर्शित किया जाता है."</string>
- <string name="color_apply" msgid="9212602012641034283">"लागू करें"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"सेेटिंग की पुष्टि करें"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"कुछ रंग सेटिंग इस डिवाइस को अनुपयोगी बना सकती हैं. इन रंग सेटिंग की पुष्टि करने के लिए ठीक क्लिक करें, अन्यथा 10 सेकंड के बाद ये सेटिंग रीसेट हो जाएंगी."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"बैटरी उपयोग"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"चार्ज किए जाने के दौरान बैटरी सेवर उपलब्ध नहीं है"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"बैटरी सेवर"</string>
diff --git a/packages/SystemUI/res/values-hr/config.xml b/packages/SystemUI/res/values-hr/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-hr/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index e1afb4d..02466d6 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -144,7 +144,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G i više"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -319,6 +321,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Ograničenje od <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Upozorenje <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Način rada"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Noćno svjetlo"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Noćno je svjetlo uključeno, dodirnite da biste ga isključili"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Noćno je svjetlo isključeno, dodirnite da biste ga uključili"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Nema nedavnih stavki"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Izbrisali ste sve"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacije o aplikaciji"</string>
@@ -332,6 +337,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podijeli vodoravno"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Podijeli okomito"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Podijeli prilagođeno"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Napunjeno"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Punjenje"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> do napunjenosti"</string>
@@ -436,6 +443,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> predstavlja dijaloški okvir za upravljanje glasnoćom"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Dodirnite da biste vratili izvornik."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Upotrebljavate radni profil"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Poziv"</item>
+ <item msgid="5997713001067658559">"Sustav"</item>
+ <item msgid="7858983209929864160">"Zvonjenje"</item>
+ <item msgid="1850038478268896762">"Mediji"</item>
+ <item msgid="8265110906352372092">"Alarm"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Dodirnite da biste uključili zvuk."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Dodirnite da biste postavili na vibraciju. Usluge pristupačnosti možda neće imati zvuk."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Dodirnite da biste isključili zvuk. Usluge pristupačnosti možda neće imati zvuk."</string>
@@ -506,21 +525,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Više postavki"</string>
<string name="notification_done" msgid="5279426047273930175">"Gotovo"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Kontrole obavijesti za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Boja i izgled"</string>
- <string name="night_mode" msgid="3540405868248625488">"Noćni način rada"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Kalibriranje zaslona"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Uključeno"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Isključeno"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Uključi automatski"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Prebacivanje na noćni način rada prema lokaciji i dobu dana"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Kada je noćni način rada uključen"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Koristi tamnu temu za OS Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Prilagodi nijansu"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Prilagodi svjetlinu"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Tamna se tema primjenjuje na glavna područja OS-a Android, kao što su, primjerice, postavke, koja se inače prikazuju u svijetloj temi."</string>
- <string name="color_apply" msgid="9212602012641034283">"Primijeni"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Potvrdite postavke"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Neke postavke boja mogu učiniti uređaj neupotrebljivim. Kliknite U redu da biste potvrdili postavke boja jer će se u suprotnom poništiti za 10 sekundi."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Potrošnja baterije"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Štednja baterije nije dostupna tijekom punjenja"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Štednja baterije"</string>
diff --git a/packages/SystemUI/res/values-hu/config.xml b/packages/SystemUI/res/values-hu/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-hu/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index ca2ca61..b89b106 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Barangolás"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> korlát"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Figyelem! <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Munka mód"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Éjszakai fény"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Éjszakai fény bekapcsolva, koppintson a kikapcsoláshoz"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Éjszakai fény kikapcsolva, koppintson a bekapcsoláshoz"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Nincsenek mostanában használt elemek"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Mindent törölt"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Az alkalmazás adatai"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Osztott vízszintes"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Osztott függőleges"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Osztott egyéni"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Feltöltve"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Töltés"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> a teljes töltöttségig"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazás kezeli a hangerőt"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Koppintson az eredeti visszaállításához."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"A munkaprofilt használja"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Hívás"</item>
+ <item msgid="5997713001067658559">"Rendszer"</item>
+ <item msgid="7858983209929864160">"Csörgetés"</item>
+ <item msgid="1850038478268896762">"Média"</item>
+ <item msgid="8265110906352372092">"Ébresztő"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Koppintson a némítás megszüntetéséhez."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Koppintson a rezgés beállításához. Előfordulhat, hogy a kisegítő lehetőségek szolgáltatásai le vannak némítva."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Koppintson a némításhoz. Előfordulhat, hogy a kisegítő lehetőségek szolgáltatásai le vannak némítva."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"További beállítások"</string>
<string name="notification_done" msgid="5279426047273930175">"Kész"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g>-értesítések vezérlői"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Szín és megjelenés"</string>
- <string name="night_mode" msgid="3540405868248625488">"Éjszakai mód"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Kijelző kalibrálása"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Bekapcsolva"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Kikapcsolva"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Automatikus bekapcsolás"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Váltás az Éjszakai módra a helynek és napszaknak megfelelően"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Amikor be van kapcsolva az Éjszakai mód"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Sötét téma használata az Android operációs rendszernél"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Színárnyalat módosítása"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Fényerő módosítása"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Sötét téma látható az Android operációs rendszer olyan alapterületeinél, amelyek normál állapotban világosan jelennek meg (például a Beállítások)."</string>
- <string name="color_apply" msgid="9212602012641034283">"Alkalmaz"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Beállítások megerősítése"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Bizonyos színbeállítások használhatatlanná tehetik ezt az eszközt. A színbeállítás megerősítéséhez kattintson az OK lehetőségre, máskülönben a rendszer 10 másodpercen belül visszaáll a korábbira."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Akkumulátorhasználat"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Az Akkumulátorkímélő módot töltés közben nem lehet használni"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Akkumulátorkímélő mód"</string>
diff --git a/packages/SystemUI/res/values-hy-rAM/config.xml b/packages/SystemUI/res/values-hy-rAM/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-hy-rAM/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index 5d01d15..d0d6c50 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Ռոումինգ"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Սահմանաչափ՝ <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> զգուշացում"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Աշխատանքային ռեժիմ"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Գիշերային լույս"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Գիշերային լույսը միացված է, հպեք՝ անջատելու համար"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Գիշերային լույսն անջատված է, հպեք՝ միացնելու համար"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Վերջին տարրեր չկան"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Դուք ջնջել եք ամենը"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Հավելվածի մասին"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Հորիզոնական տրոհում"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Ուղղահայաց տրոհում"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Հատուկ տրոհում"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Լիցքավորված է"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Լիցքավորվում է"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Լրիվ լիցքավորմանը մնաց <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
@@ -423,7 +430,7 @@
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Կոծկել"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Էկրանն ամրացված է"</string>
<string name="screen_pinning_description" msgid="7238941806855968768">"Էկրանը կմնա տեսադաշտում, մինչև այն ապամրացնեք: Ապամրացնելու համար հպեք և պահեք Հետ կոճակը:"</string>
- <string name="screen_pinning_positive" msgid="3783985798366751226">"Հասկանալի է"</string>
+ <string name="screen_pinning_positive" msgid="3783985798366751226">"Եղավ"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Ոչ, շնորհակալություն"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Թաքցնե՞լ <xliff:g id="TILE_LABEL">%1$s</xliff:g>-ը:"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Այն դարձյալ կհայտնվի, երբ նորից միացնեք կարգավորումներում:"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ը ձայնի ուժգնության երկխոսության հավելված է"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Հպեք՝ բնօրինակը վերականգնելու համար:"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Դուք օգտագործում եք ձեր աշխատանքային պրոֆիլը"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Զանգել"</item>
+ <item msgid="5997713001067658559">"Համակարգ"</item>
+ <item msgid="7858983209929864160">"Զանգ"</item>
+ <item msgid="1850038478268896762">"Մեդիա"</item>
+ <item msgid="8265110906352372092">"Զարթուցիչ"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s: Հպեք՝ ձայնը միացնելու համար:"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s: Հպեք՝ թրթռումը միացնելու համար: Մատչելիության ծառայությունների ձայնը կարող է անջատվել:"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s: Հպեք՝ ձայնն անջատելու համար: Մատչելիության ծառայությունների ձայնը կարող է անջատվել:"</string>
@@ -464,7 +483,7 @@
<string name="tuner_warning_title" msgid="7094689930793031682">"Զվարճանք մեկ՝ որոշակի մարդու համար"</string>
<string name="tuner_warning" msgid="8730648121973575701">"Համակարգի ՕՄ-ի ընդունիչը հնարավորություն է տալիս հարմարեցնել Android-ի օգտվողի միջերեսը: Այս փորձնական գործառույթները կարող են հետագա թողարկումների մեջ փոփոխվել, խափանվել կամ ընդհանրապես չհայտնվել: Եթե շարունակում եք, զգուշացեք:"</string>
<string name="tuner_persistent_warning" msgid="8597333795565621795">"Այս փորձնական գործառույթները կարող են հետագա թողարկումների մեջ փոփոխվել, խափանվել կամ ընդհանրապես չհայտնվել: Եթե շարունակում եք, զգուշացեք:"</string>
- <string name="got_it" msgid="2239653834387972602">"Հասկանալի է"</string>
+ <string name="got_it" msgid="2239653834387972602">"Եղավ"</string>
<string name="tuner_toast" msgid="603429811084428439">"Համակարգի ՕՄ-ի ընդունիչը ավելացվել է կարգավորումներին"</string>
<string name="remove_from_settings" msgid="8389591916603406378">"Հեռացնել կարգավորումներից"</string>
<string name="remove_from_settings_prompt" msgid="6069085993355887748">"Հեռացնե՞լ Համակարգի ՕՄ-ի ընդունիչը կարգավորումներից և չօգտվել այլևս նրա գործառույթներից:"</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Այլ կարգավորումներ"</string>
<string name="notification_done" msgid="5279426047273930175">"Պատրաստ է"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի ծանուցումների կառավարներ"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Գույնը և արտաքին տեսքը"</string>
- <string name="night_mode" msgid="3540405868248625488">"Գիշերային ռեժիմ"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Չափաբերել էկրանը"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Միացված է"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Անջատված է"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Միացնել ավտոմատ կերպով"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Անցեք Գիշերային ռեժիմի, եթե դա պահանջում է տեղը և օրվա ժամանակը"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Երբ Գիշերային ռեժիմը միացված է"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Օգտագործել մուգ թեման Android OS-ի համար"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Կարգավորել երանգը"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Կարգավորել պայծառությունը"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Մուգ թեման կիրառվում է Android OS-ի հիմնական հատվածների նկատմամբ, որոնք սովորաբար ցուցադրվում են բաց թեմայում (օրինակ՝ Կարգավորումներ):"</string>
- <string name="color_apply" msgid="9212602012641034283">"Կիրառել"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Հաստատել կարգավորումները"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Գունային որոշ կարգավորումները կարող են այս սարքը օգտագործման համար ոչ պիտանի դարձնել: Սեղմեք Լավ կոճակը՝ գունային այս կարգավորումները հաստատելու համար: Հակառակ դեպքում այս կարգավորումները կվերակայվեն 10 վայրկյան հետո:"</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Մարտկոցի օգտագործում"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Մարտկոցի տնտեսումը լիցքավորման ժամանակ հասանելի չէ"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Մարտկոցի տնտեսում"</string>
diff --git a/packages/SystemUI/res/values-in/config.xml b/packages/SystemUI/res/values-in/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-in/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 293cd00..ad24d6a 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Batas <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Peringatan <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Mode kerja"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Cahaya Malam"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Cahaya Malam hidup, ketuk untuk mematikan"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Cahaya Malam mati, ketuk untuk menyalakan"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Tidak ada item baru-baru ini"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Anda sudah menghapus semua"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Info Aplikasi"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Pisahkan Horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Pisahkan Vertikal"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Pisahkan Khusus"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Terisi"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Mengisi daya"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> sampai penuh"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> adalah dialog volume"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Ketuk untuk memulihkan aslinya."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Anda menggunakan profil kerja"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Telepon"</item>
+ <item msgid="5997713001067658559">"Sistem"</item>
+ <item msgid="7858983209929864160">"Dering"</item>
+ <item msgid="1850038478268896762">"Media"</item>
+ <item msgid="8265110906352372092">"Alarm"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Ketuk untuk menyuarakan."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Ketuk untuk menyetel agar bergetar. Layanan aksesibilitas mungkin dibisukan."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Ketuk untuk membisukan. Layanan aksesibilitas mungkin dibisukan."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Setelan lainnya"</string>
<string name="notification_done" msgid="5279426047273930175">"Selesai"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Kontrol notifikasi <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Warna dan tampilan"</string>
- <string name="night_mode" msgid="3540405868248625488">"Mode malam"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Kalibrasi layar"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Aktif"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Nonaktif"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Aktifkan secara otomatis"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Beralih ke Mode Malam yang sesuai untuk lokasi dan waktu"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Saat Mode Malam aktif"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Gunakan tema gelap untuk OS Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Sesuaikan rona"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Sesuaikan kecerahan"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Tema gelap diterapkan ke area inti OS Android yang biasanya ditampilkan di tema terang, seperti Setelan."</string>
- <string name="color_apply" msgid="9212602012641034283">"Terapkan"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Konfirmasi setelan"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Beberapa setelan warna dapat membuat perangkat ini tidak dapat digunakan. Klik OKE untuk mengonfirmasi setelan warna ini. Jika tidak, setelan ini akan disetel ulang setelah 10 detik."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Pemakaian baterai"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Penghemat Baterai tidak tersedia selama pengisian daya"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Penghemat Baterai"</string>
diff --git a/packages/SystemUI/res/values-is-rIS/config.xml b/packages/SystemUI/res/values-is-rIS/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-is-rIS/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml
index 05e6256..a95e28b 100644
--- a/packages/SystemUI/res/values-is-rIS/strings.xml
+++ b/packages/SystemUI/res/values-is-rIS/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Reiki"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> hámark"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> viðvörun"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Vinnustilling"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Næturljós"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Kveikt á næturljósi, ýttu til að slökkva"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Slökkt á næturljósi, ýttu til að kveikja"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Engin nýleg atriði"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Þú hefur hreinsað allt"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Forritsupplýsingar"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Lárétt skipting"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Lóðrétt skipting"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Sérsniðin skipting"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Fullhlaðin"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Í hleðslu"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> þar til fullri hleðslu er náð"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> er hljóðstyrksvalmyndin"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Ýttu til að færa í upprunalegt horf."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Þú ert að nota vinnusniðið"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Hringja"</item>
+ <item msgid="5997713001067658559">"Kerfi"</item>
+ <item msgid="7858983209929864160">"Hringing"</item>
+ <item msgid="1850038478268896762">"Margmiðlun"</item>
+ <item msgid="8265110906352372092">"Vekjari"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Ýttu til að hætta að þagga."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Ýttu til að stilla á titring. Hugsanlega verður slökkt á hljóði aðgengisþjónustu."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Ýttu til að þagga. Hugsanlega verður slökkt á hljóði aðgengisþjónustu."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Fleiri stillingar"</string>
<string name="notification_done" msgid="5279426047273930175">"Lokið"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Tilkynningastýringar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Litur og útlit"</string>
- <string name="night_mode" msgid="3540405868248625488">"Næturstilling"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Kvarða skjáinn"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Kveikt"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Slökkt"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Kveikja sjálfkrafa"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Skipta í næturstillingu í samræmi við staðsetningu og tíma dags"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Þegar kveikt er á næturstillingu"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Nota dökkt þema fyrir Android OS"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Stilla litblæ"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Stilla birtustig"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Dökka þemað er notað á þeim aðalsvæðum Android OS sem venjulega eru ljós, s.s. í stillingum."</string>
- <string name="color_apply" msgid="9212602012641034283">"Nota"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Staðfesta stillingar"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Sumar litastillingar kunna að bitna á notagildi tækisins. Veldu „Í lagi“ til að staðfesta þessar litastillingar, að öðrum kosti verða litirnir endurstilltir eftir tíu sekúndur."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Rafhlöðunotkun"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Ekki er hægt að nota rafhlöðusparnað meðan á hleðslu stendur"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Rafhlöðusparnaður"</string>
diff --git a/packages/SystemUI/res/values-it/config.xml b/packages/SystemUI/res/values-it/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-it/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index e8aacd5..d370b65 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -319,6 +321,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Limite di <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Avviso <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Modalità Lavoro"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Luminosità notturna"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Luminosità notturna attiva, tocca per disattivarla"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Luminosità notturna disattivata, tocca per attivarla"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Nessun elemento recente"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Hai cancellato tutto"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informazioni sull\'applicazione"</string>
@@ -332,6 +337,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisione in orizzontale"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisione in verticale"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisione personalizzata"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carica"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"In carica"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> al termine della carica"</string>
@@ -436,6 +443,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> rappresenta la finestra di dialogo relativa al volume"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Tocca per ripristinare l\'originale."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Stai utilizzando il profilo di lavoro"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Chiamata"</item>
+ <item msgid="5997713001067658559">"Sistema"</item>
+ <item msgid="7858983209929864160">"Suoneria"</item>
+ <item msgid="1850038478268896762">"Contenuti multimediali"</item>
+ <item msgid="8265110906352372092">"Sveglia"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Tocca per riattivare l\'audio."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tocca per attivare la vibrazione. L\'audio dei servizi di accessibilità può essere disattivato."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Tocca per disattivare l\'audio. L\'audio dei servizi di accessibilità può essere disattivato."</string>
@@ -506,21 +525,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Altre impostazioni"</string>
<string name="notification_done" msgid="5279426047273930175">"Fine"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Controlli di notifica per <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Colore e aspetto"</string>
- <string name="night_mode" msgid="3540405868248625488">"Modalità notturna"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Calibra display"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Attiva"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Disattivata"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Attiva automaticamente"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Attiva la modalità notturna in base alla località e all\'ora del giorno"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Quando la modalità notturna è attiva"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Utilizza tema scuro per sistema Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Regola tinta"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Regola luminosità"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Il tema scuro viene applicato agli elementi fondamentali del sistema operativo Android che vengono generalmente visualizzati con un tema chiaro, ad esempio le impostazioni."</string>
- <string name="color_apply" msgid="9212602012641034283">"Applica"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Conferma le impostazioni"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Alcune impostazioni relative ai colori potrebbero rendere inutilizzabile il dispositivo. Fai clic su OK per confermare queste impostazioni; in caso contrario, le impostazioni verranno reimpostate dopo 10 secondi."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Utilizzo batteria"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Risparmio energetico non disponibile durante la ricarica"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Risparmio energetico"</string>
diff --git a/packages/SystemUI/res/values-iw/config.xml b/packages/SystemUI/res/values-iw/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-iw/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 2f1393e..4cf48c2 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -145,7 +145,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"+4G"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"+LTE"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"נדידה"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"קצה"</string>
@@ -321,6 +323,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"הגבלה של <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"אזהרה - <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"מצב עבודה"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"תאורת לילה"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"תאורת לילה פועלת, הקש כדי לכבות"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"תאורת לילה כבויה, הקש כדי להפעיל"</string>
<string name="recents_empty_message" msgid="808480104164008572">"אין פריטים אחרונים"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"מחקת הכול"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"מידע על האפליקציה"</string>
@@ -334,6 +339,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"פיצול אופקי"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"פיצול אנכי"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"פיצול מותאם אישית"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"טעון"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"טוען"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> עד למילוי"</string>
@@ -438,6 +445,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> הוא תיבת הדו-שיח של עוצמת הקול"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"הקש כדי לשחזר את המקור."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"אתה משתמש בפרופיל העבודה שלך"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"שיחה"</item>
+ <item msgid="5997713001067658559">"מערכת"</item>
+ <item msgid="7858983209929864160">"השמע צלצול"</item>
+ <item msgid="1850038478268896762">"מדיה"</item>
+ <item msgid="8265110906352372092">"התראה"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. הקש כדי לבטל את ההשתקה."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. הקש כדי להגדיר רטט. ייתכן ששירותי הנגישות מושתקים."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. הקש כדי להשתיק. ייתכן ששירותי הנגישות מושתקים."</string>
@@ -508,21 +527,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"הגדרות נוספות"</string>
<string name="notification_done" msgid="5279426047273930175">"סיום"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> פקדי הודעות"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"צבע ומראה"</string>
- <string name="night_mode" msgid="3540405868248625488">"מצב לילה"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"כיול תצוגה"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"פועל"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"כבוי"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"הפעל אוטומטית"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"החלף למצב לילה בהתאם למיקום ולשעה ביום"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"כאשר מצב לילה מופעל"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"השתמש בעיצוב כהה למערכת ההפעלה של Android."</string>
- <string name="adjust_tint" msgid="3398569573231409878">"התאמת גוון"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"התאמת בהירות"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"העיצוב הכהה מוחל על התחומים העיקריים במערכת ההפעלה Android שמוצגים בדרך כלל בעיצוב בהיר, כמו \'הגדרות\'."</string>
- <string name="color_apply" msgid="9212602012641034283">"החל"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"אישור הגדרות"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"הגדרות צבע מסוימות עלולות להפוך את המכשיר הזה לבלתי שמיש. לחץ על אישור כדי לאשר את הגדרות הצבע האלה, אחרת הגדרות אלה יתאפסו לאחר 10 שניות."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"שימוש בסוללה"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"תכונת החיסכון בסוללה אינה זמינה בעת טעינת המכשיר"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"חיסכון בסוללה"</string>
diff --git a/packages/SystemUI/res/values-ja/config.xml b/packages/SystemUI/res/values-ja/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-ja/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 39b9ea7..c42e346 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"ローミング中"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"EDGE"</string>
@@ -319,6 +321,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"上限: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"警告: 上限は<xliff:g id="DATA_LIMIT">%s</xliff:g>です"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Work モード"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"読書灯"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"読書灯 ON: タップすると OFF になります"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"読書灯 OFF: タップすると ON になります"</string>
<string name="recents_empty_message" msgid="808480104164008572">"最近のタスクはありません"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"すべてのタスクを消去しました"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"アプリ情報"</string>
@@ -332,6 +337,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"横に分割"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"縦に分割"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"分割(カスタム)"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"充電が完了しました"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"充電しています"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"充電完了まで<xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
@@ -436,6 +443,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g>を音量ダイアログとして使用"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"タップすると元に戻ります。"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"仕事用プロファイルを使用しています"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"通話"</item>
+ <item msgid="5997713001067658559">"システム"</item>
+ <item msgid="7858983209929864160">"着信音"</item>
+ <item msgid="1850038478268896762">"メディア"</item>
+ <item msgid="8265110906352372092">"アラーム"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s。タップしてミュートを解除します。"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s。タップしてバイブレーションに設定します。ユーザー補助機能サービスがミュートされる場合があります。"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s。タップしてミュートします。ユーザー補助機能サービスがミュートされる場合があります。"</string>
@@ -506,21 +525,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"詳細設定"</string>
<string name="notification_done" msgid="5279426047273930175">"完了"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」の通知の管理"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"色と表示"</string>
- <string name="night_mode" msgid="3540405868248625488">"夜間モード"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"表示の調整"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"ON"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"OFF"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"自動的に ON"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"場所や時間に応じて夜間モードに切り替えます"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"夜間モードが ON のとき"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android OS でダークテーマを使用"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"ティントを調整"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"明るさを調整"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"通常ライトテーマで表示される Android OS の主要領域(設定など)にダークテーマが適用されます。"</string>
- <string name="color_apply" msgid="9212602012641034283">"適用"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"設定の確認"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"一部の色設定を適用すると、この端末を使用できなくなることがあります。この色設定を確認するには、[OK] をクリックしてください。確認しない場合、10 秒後に設定はリセットされます。"</string>
<string name="battery_panel_title" msgid="7944156115535366613">"電池の使用状況"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"充電中はバッテリー セーバーは利用できません"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"バッテリー セーバー"</string>
diff --git a/packages/SystemUI/res/values-ka-rGE/config.xml b/packages/SystemUI/res/values-ka-rGE/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-ka-rGE/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 378b36c..6ddcde8 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5გბ"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"როუმინგი"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"ლიმიტი: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> გაფრთხილება"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"სამსახურის რეჟიმი"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"ღამის განათება"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"ღამის განათება ჩართულია, შეეხეთ გამოსართავად"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"ღამის განათება გამორთულია, შეეხეთ ჩასართავად"</string>
<string name="recents_empty_message" msgid="808480104164008572">"ბოლოს გამოყენებული ერთეულები არ არის"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"ყველაფერი გასუფთავდა"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"აპლიკაციის შესახებ"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ჰორიზონტალური გაყოფა"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ვერტიკალური გაყოფა"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ინდივიდუალური გაყობა"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"დატენილია"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"მიმდინარეობს დატენვა"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> სრულად დატენვამდე"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ხმოვან დიალოგშია"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"შეეხეთ ორიგინალის აღსადგენად."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"თქვენ სამსახურის პროფილს იყენებთ"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"დარეკვა"</item>
+ <item msgid="5997713001067658559">"სისტემა"</item>
+ <item msgid="7858983209929864160">"ზარი"</item>
+ <item msgid="1850038478268896762">"მედია"</item>
+ <item msgid="8265110906352372092">"მაღვიძარა"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. შეეხეთ დადუმების გასაუქმებლად."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. შეეხეთ ვიბრაციაზე დასაყენებლად. შეიძლება დადუმდეს მარტივი წვდომის სერვისებიც."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. შეეხეთ დასადუმებლად. შეიძლება დადუმდეს მარტივი წვდომის სერვისებიც."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"დამატებითი პარამეტრები"</string>
<string name="notification_done" msgid="5279426047273930175">"მზადაა"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> შეტყობინებების მართვის საშუალებები"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"ფერი და იერსახე"</string>
- <string name="night_mode" msgid="3540405868248625488">"ღამის რეჟიმი"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"ეკრანის კალიბრაცია"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"ჩართული"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"გამორთული"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"ავტომატურად ჩართვა"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"ღამის რეჟიმზე გადართვა მდებარეობისა და დღე-ღამის მონაკვეთის შესაბამისად."</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"ღამის რეჟიმის ჩართვისას"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android OS-ისთვის მუქი თემის გამოყენება"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"ელფერის გასწორება"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"სიკაშკაშის გასწორება"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"მუქი თემა მიესადაგება Android OS-ის ძირითად არეებს, რომლებიც, ჩვეულებრივ, ღია თემის მეშვეობით არის ნაჩვენები. მაგალითად, პარამეტრებს."</string>
- <string name="color_apply" msgid="9212602012641034283">"გამოყენება"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"პარამეტრების დადასტურება"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"ფერთა ზოგიერთ პარამეტრს ამ მოწყობილობასთან მუშაობის გართულება შეუძლია. ფერთა ამჟამინდელი პარამეტრების დასადასტურებლად, დააწკაპუნეთ „კარგი“-ზე. წინააღმდეგ შემთხვევაში, პარამეტრები 10 წამის შემდეგ ჩამოიყრება."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"ბატარეის მოხმარება"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ბატარეის დამზოგი დატენვისას მიწვდომელია"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"ბატარეის დამზოგი"</string>
diff --git a/packages/SystemUI/res/values-kk-rKZ/config.xml b/packages/SystemUI/res/values-kk-rKZ/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-kk-rKZ/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml
index 1e1544b..2c444e4 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3Г"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3,5Г"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4Г"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"ҰМД"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA (кодтармен бөлінген бірнеше қол жетімділік)"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Роуминг"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"EDGE (ұялы байланыстар жүйесіне арналған жетілдірілген деректер шамалары)"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> шегі"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> туралы ескерту"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Жұмыс режимі"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Түнгі жарық"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Түнгі жарық қосулы, өшіру үшін оны түртіңіз"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Түнгі жарық өшірулі, қосу үшін оны түртіңіз"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Жақындағы элементтер жоқ"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Сіз барлығын өшірдіңіз"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Қолданба туралы ақпарат"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Бөлінген көлденең"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Бөлінген тік"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Бөлінген теңшелетін"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Зарядталды"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Зарядталуда"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Толғанға дейін <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> — көлем диалогтық терезесі"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Бастапқы қалпына келтіру үшін түртіңіз."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Сіз жұмыс профиліңізді пайдаланып жатырсыз"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Қоңырау шалу"</item>
+ <item msgid="5997713001067658559">"Жүйе"</item>
+ <item msgid="7858983209929864160">"Шылдырлау"</item>
+ <item msgid="1850038478268896762">"Мультимeдиа"</item>
+ <item msgid="8265110906352372092">"Дабыл"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Дыбысын қосу үшін түртіңіз."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Діріл режимін орнату үшін түртіңіз. Арнайы мүмкіндік қызметтерінің дыбысы өшуі мүмкін."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Дыбысын өшіру үшін түртіңіз. Арнайы мүмкіндік қызметтерінің дыбысы өшуі мүмкін."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Қосымша параметрлер"</string>
<string name="notification_done" msgid="5279426047273930175">"Дайын"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> хабарландыруларды басқару элементтері"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Түс және сыртқы түрі"</string>
- <string name="night_mode" msgid="3540405868248625488">"Түнгі режим"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Дисплейді калибрлеу"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Қосулы"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Өшірулі"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Автоматты түрде қосу"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Орынға және күн уақытына тиісті түнгі режимге ауысу"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Түнгі режим қосулы кезде"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android ОЖ үшін күңгірт тақырыпты пайдалану"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Реңкті реттеу"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Жарықтықты реттеу"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Күңгірт тақырып Android операциялық жүйесінің әдетте \"Параметрлер\" сияқты ашық тақырыпта көрсетілетін негізгі аумақтарына қолданылады."</string>
- <string name="color_apply" msgid="9212602012641034283">"Қолдану"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Параметрлерді растау"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Кейбір түс параметрлері бұл құрылғыны пайдалану мүмкін емес етуі мүмкін. Бұл түс параметрлерін растау үшін OK түймесін басыңыз, әйтпесе параметрлер 10 секундтан кейін ысырылады."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Батареяны пайдалану"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Зарядтау кезінде Батарея үнемдегіш қол жетімді емес"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Батарея үнемдегіш"</string>
diff --git a/packages/SystemUI/res/values-km-rKH/config.xml b/packages/SystemUI/res/values-km-rKH/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-km-rKH/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index 84d3470..0db7abc 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"រ៉ូមីង"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"ដែនកំណត់ <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ការព្រមាន"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"របៀបការងារ"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"ពន្លឺពេលយប់"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"ពន្លឺពេលយប់បើកហើយ ប៉ះដើម្បីបិទ"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"ពន្លឺពេលយប់បិទហើយ ប៉ះដើម្បីបើក"</string>
<string name="recents_empty_message" msgid="808480104164008572">"មិនមានធាតុថ្មីៗទេ"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"អ្នកបានជម្រះអ្វីៗទាំងអស់"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"ព័ត៌មានកម្មវិធី"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"បំបែកផ្តេក"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"បំបែកបញ្ឈរ"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"បំបែកផ្ទាល់ខ្លួន"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"បានបញ្ចូលថ្ម"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"កំពុងបញ្ចូលថ្ម"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> រហូតដល់ពេញ"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> គឺជាប្រអប់សម្លេង"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"ប៉ះដើម្បីស្តារច្បាប់ដើម"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"អ្នកកំពុងប្រើប្រវត្តិរូបការងាររបស់អ្នក"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"ហៅ"</item>
+ <item msgid="5997713001067658559">"ប្រព័ន្ធ"</item>
+ <item msgid="7858983209929864160">"រោទ៍"</item>
+ <item msgid="1850038478268896762">"មេឌៀ"</item>
+ <item msgid="8265110906352372092">"ម៉ោងរោទ៍"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"ប៊្លូធូស"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s។ ប៉ះដើម្បីបើកសំឡេង។"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s។ ប៉ះដើម្បីកំណត់ឲ្យញ័រ។ សេវាកម្មលទ្ធភាពប្រើប្រាស់អាចនឹងត្រូវបានបិទសំឡេង។"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s។ ប៉ះដើម្បីបិទសំឡេង។ សេវាកម្មលទ្ធភាពប្រើប្រាស់អាចនឹងត្រូវបានបិទសំឡេង។"</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"ការកំណត់ច្រើនទៀត"</string>
<string name="notification_done" msgid="5279426047273930175">"រួចរាល់"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"អង្គគ្រប់គ្រងការជូនដំណឹង <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"ពណ៌ និងរូបរាង"</string>
- <string name="night_mode" msgid="3540405868248625488">"របៀបពេលយប់"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"ការបង្ហាញក្រិត"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"បើក"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"បិទ"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"បើកដោយស្វ័យប្រវត្តិ"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"ប្តូរទៅជារបៀបពេលយប់ដែលសមស្របទៅតាមទីកន្លែង និងពេលវេលា"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"នៅពេលបើករបៀបពេលយប់"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"ប្រើធីមងងឹតសម្រាប់ប្រព័ន្ធប្រតិបត្តិការ Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"កែសម្រួលពណ៌ព្រឿងៗ"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"កែសម្រួលពន្លឺ"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"ធីមងងឹតត្រូវបានប្រើសម្រាប់ចំណុចស្នូលនៃប្រព័ន្ធប្រតិបត្តិការ Android ដែលជាទូទៅត្រូវបានបង្ហាញជាធីមភ្លឺ ដូចជាការកំណត់ជាដើម។"</string>
- <string name="color_apply" msgid="9212602012641034283">"អនុវត្ត"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"បញ្ជាក់ការកំណត់"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"ការកំណត់ពណ៌មួយចំនួនអាចធ្វើឲ្យឧបករណ៍នេះមិនអាចប្រើបាន។ សូមចុច យល់ព្រម ដើម្បីបញ្ជាក់ការកំណត់ពណ៌ទាំងនេះ បើមិនដូច្នេះទេការកំណត់ទាំងនេះនឹងកំណត់ឡើងវិញក្នុងរយៈពេល 10 វិនាទីបន្ទាប់។"</string>
<string name="battery_panel_title" msgid="7944156115535366613">"ការប្រើប្រាស់ថ្ម"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"កម្មវិធីសន្សំថ្មមិនអាចប្រើបានអំឡុងពេលសាកថ្មទេ"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"កម្មវិធីសន្សំថ្ម"</string>
diff --git a/packages/SystemUI/res/values-kn-rIN/config.xml b/packages/SystemUI/res/values-kn-rIN/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-kn-rIN/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml
index ed48bcb..c62a8db 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"ರೋಮಿಂಗ್"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"ಎಡ್ಜ್"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ಮಿತಿ"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ಎಚ್ಚರಿಕೆ"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"ಕೆಲಸದ ಮೋಡ್"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"ನೈಟ್ ಲೈಟ್"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"ನೈಟ್ ಲೈಟ್ ಆನ್ ಆಗಿದೆ, ಆಫ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"ನೈಟ್ ಲೈಟ್ ಆಫ್ ಆಗಿದೆ, ಆನ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="recents_empty_message" msgid="808480104164008572">"ಯಾವುದೇ ಇತ್ತೀಚಿನ ಐಟಂಗಳಿಲ್ಲ"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"ನೀವು ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿರುವಿರಿ"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ಅಡ್ಡಲಾಗಿ ವಿಭಜಿಸಿದ"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ಲಂಬವಾಗಿ ವಿಭಜಿಸಿದ"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ಕಸ್ಟಮ್ ವಿಭಜಿಸಿದ"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"ಚಾರ್ಜ್ ಆಗಿದೆ"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> ಪೂರ್ಣಗೊಳ್ಳುವವರೆಗೆ"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ವಾಲ್ಯೂಮ್ ಸಂವಾದವಾಗಿದೆ"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"ಮೂಲಕ್ಕೆ ಮರುಸ್ಥಾಪಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"ನಿಮ್ಮ ಕೆಲಸದ ಪ್ರೊಫೈಲ್ ಅನ್ನು ನೀವು ಬಳಸುತ್ತಿರುವಿರಿ"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"ಕರೆಮಾಡಿ"</item>
+ <item msgid="5997713001067658559">"ಸಿಸ್ಟಂ"</item>
+ <item msgid="7858983209929864160">"ಉಂಗುರ"</item>
+ <item msgid="1850038478268896762">"ಮಾಧ್ಯಮ"</item>
+ <item msgid="8265110906352372092">"ಅಲಾರಮ್"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"ಬ್ಲೂಟೂತ್"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. ಅನ್ಮ್ಯೂಟ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. ಕಂಪನಕ್ಕೆ ಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಪ್ರವೇಶಿಸುವಿಕೆ ಸೇವೆಗಳನ್ನು ಮ್ಯೂಟ್ ಮಾಡಬಹುದು."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. ಮ್ಯೂಟ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಪ್ರವೇಶಿಸುವಿಕೆ ಸೇವೆಗಳನ್ನು ಮ್ಯೂಟ್ ಮಾಡಬಹುದು."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"ಹೆಚ್ಚಿನ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="notification_done" msgid="5279426047273930175">"ಮುಗಿದಿದೆ"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಅಧಿಸೂಚನೆ ನಿಯಂತ್ರಣಗಳು"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"ಬಣ್ಣ ಮತ್ತು ಗೋಚರತೆ"</string>
- <string name="night_mode" msgid="3540405868248625488">"ರಾತ್ರಿ ಮೋಡ್"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"ಅಣಿಗೊಳಿಸುವ ಪ್ರದರ್ಶನ"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"ಆನ್"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"ಆಫ್"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಆನ್ ಮಾಡು"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"ಸ್ಥಳ ಮತ್ತು ದಿನದ ಸಮಯಕ್ಕೆ ಸೂಕ್ತವಾಗುವಂತೆ ರಾತ್ರಿ ಮೋಡ್ ಅನ್ನು ಬದಲಾಯಿಸಿ"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"ರಾತ್ರಿ ಮೋಡ್ ಆನ್ ಆಗಿರುವಾಗ"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android OS ಗೆ ಕಪ್ಪು ಥೀಮ್ ಬಳಸಿ"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"ಟಿಂಟ್ ಸರಿಹೊಂದಿಸು"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"ಪ್ರಖರತೆಯನ್ನು ಸರಿಹೊಂದಿಸು"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"ಕಪ್ಪು ಥೀಮ್ ಅನ್ನು Android OS ನ ಕೋರ್ ಪ್ರದೇಶಗಳಿಗೆ ಅನ್ವಯಿಸಲಾಗಿರುತ್ತದೆ. ಇದನ್ನು ಸೆಟ್ಟಿಂಗ್ಗಳಂತಹ ತಿಳಿಯಾದ ಥೀಮ್ನಲ್ಲಿ ಸಾಮಾನ್ಯವಾಗಿ ಪ್ರದರ್ಶಿಸಲಾಗುತ್ತದೆ."</string>
- <string name="color_apply" msgid="9212602012641034283">"ಅನ್ವಯಿಸು"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಖಚಿತಪಡಿಸಿ"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"ಕೆಲವು ಬಣ್ಣ ಸೆಟ್ಟಿಂಗ್ಗಳು ಈ ಸಾಧನವನ್ನು ಅನುಪಯುಕ್ತಗೊಳಿಸಬಹುದು. ಈ ಬಣ್ಣ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಖಚಿತಪಡಿಸಲು ಸರಿ ಕ್ಲಿಕ್ ಮಾಡಿ, ಇಲ್ಲವಾದರೆ ಈ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು 10 ಸೆಕೆಂಡುಗಳ ನಂತರ ಮರುಹೊಂದಿಸಿ."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"ಬ್ಯಾಟರಿ ಬಳಕೆ"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ಚಾರ್ಜಿಂಗ್ ಸಮಯದಲ್ಲಿ ಬ್ಯಾಟರಿ ಸೇವರ್ ಲಭ್ಯವಿರುವುದಿಲ್ಲ"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"ಬ್ಯಾಟರಿ ಸೇವರ್"</string>
@@ -601,7 +605,7 @@
<string name="preview" msgid="9077832302472282938">"ಪೂರ್ವವೀಕ್ಷಣೆ"</string>
<string name="drag_to_add_tiles" msgid="7058945779098711293">"ಟೈಲ್ಗಳನ್ನು ಸೇರಿಸಲು ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"ತೆಗೆದುಹಾಕಲು ಇಲ್ಲಿ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
- <string name="qs_edit" msgid="2232596095725105230">"ಸಂಪಾದಿಸು"</string>
+ <string name="qs_edit" msgid="2232596095725105230">"ಎಡಿಟ್"</string>
<string name="tuner_time" msgid="6572217313285536011">"ಸಮಯ"</string>
<string-array name="clock_options">
<item msgid="5965318737560463480">"ಗಂಟೆಗಳು, ನಿಮಿಷಗಳು, ಸೆಕೆಂಡುಗಳನ್ನು ತೋರಿಸು"</item>
@@ -625,7 +629,7 @@
<string name="accessibility_action_divider_top_50" msgid="6385859741925078668">"50% ಮೇಲಕ್ಕೆ"</string>
<string name="accessibility_action_divider_top_30" msgid="6201455163864841205">"30% ಮೇಲಕ್ಕೆ"</string>
<string name="accessibility_action_divider_bottom_full" msgid="301433196679548001">"ಕೆಳಗಿನ ಪೂರ್ಣ ಪರದೆ"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"ಸ್ಥಾನ <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. ಸಂಪಾದಿಸಲು ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"ಸ್ಥಾನ <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. ಎಡಿಟ್ ಮಾಡಲು ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="accessibility_qs_edit_add_tile_label" msgid="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. ಸೇರಿಸಲು ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"ಸ್ಥಾನ <xliff:g id="POSITION">%1$d</xliff:g>. ಆಯ್ಕೆಮಾಡಲು ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ಸರಿಸಿ"</string>
@@ -633,7 +637,7 @@
<string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="POSITION">%2$d</xliff:g> ಸ್ಥಾನಕ್ಕೆ <xliff:g id="TILE_NAME">%1$s</xliff:g> ಸೇರಿಸಲಾಗಿದೆ"</string>
<string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string>
<string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="POSITION">%2$d</xliff:g> ಸ್ಥಾನಕ್ಕೆ <xliff:g id="TILE_NAME">%1$s</xliff:g> ಸೇರಿಸಲಾಗಿದೆ"</string>
- <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳ ಸಂಪಾದಕ."</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳ ಎಡಿಟರ್."</string>
<string name="accessibility_desc_notification_icon" msgid="8352414185263916335">"<xliff:g id="ID_1">%1$s</xliff:g> ಅಧಿಸೂಚನೆ: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="dock_forced_resizable" msgid="5914261505436217520">"ವಿಭಜಿಸಿದ ಪರದೆಯಲ್ಲಿ ಅಪ್ಲಿಕೇಶನ್ ಕೆಲಸ ಮಾಡದೇ ಇರಬಹುದು."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="3871617304250207291">"ಅಪ್ಲಿಕೇಶನ್ ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string>
@@ -645,6 +649,6 @@
<string name="accessibility_quick_settings_no_internet" msgid="31890692343084075">"ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ."</string>
<string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"ವಿವರಗಳನ್ನು ತೆರೆಯಿರಿ."</string>
<string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ತೆರೆಯಿರಿ."</string>
- <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ಸೆಟ್ಟಿಂಗ್ಗಳ ಕ್ರಮವನ್ನು ಸಂಪಾದಿಸು."</string>
+ <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ಸೆಟ್ಟಿಂಗ್ಗಳ ಕ್ರಮವನ್ನು ಎಡಿಟ್ ಮಾಡಿ."</string>
<string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g> ರಲ್ಲಿ <xliff:g id="ID_1">%1$d</xliff:g> ಪುಟ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/config.xml b/packages/SystemUI/res/values-ko/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-ko/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 5c46e00..3c08e92 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G 이상"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"로밍"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -319,6 +321,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"한도: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> 경고"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"작업 모드"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"야간 조명"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"야간 조명 사용 설정됨, 사용 중지하려면 탭"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"야간 조명 사용 중지됨, 사용 설정하려면 탭"</string>
<string name="recents_empty_message" msgid="808480104164008572">"최근 항목이 없습니다."</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"모든 항목을 삭제했습니다."</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"애플리케이션 정보"</string>
@@ -332,6 +337,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"수평 분할"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"수직 분할"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"맞춤 분할"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"충전됨"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"충전 중"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"완충까지 <xliff:g id="CHARGING_TIME">%s</xliff:g> 남음"</string>
@@ -436,6 +443,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g>은(는) 볼륨 대화입니다."</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"원본을 복원하려면 탭하세요."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"직장 프로필을 사용하고 있습니다."</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"통화"</item>
+ <item msgid="5997713001067658559">"시스템"</item>
+ <item msgid="7858983209929864160">"벨 울리기"</item>
+ <item msgid="1850038478268896762">"미디어"</item>
+ <item msgid="8265110906352372092">"알람"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"블루투스"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. 탭하여 음소거를 해제하세요."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. 탭하여 진동으로 설정하세요. 접근성 서비스가 음소거될 수 있습니다."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. 탭하여 음소거로 설정하세요. 접근성 서비스가 음소거될 수 있습니다."</string>
@@ -506,21 +525,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"설정 더보기"</string>
<string name="notification_done" msgid="5279426047273930175">"완료"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> 알림 관리"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"색상 및 모양"</string>
- <string name="night_mode" msgid="3540405868248625488">"야간 모드"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"디스플레이 보정"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"사용"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"사용 안함"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"자동으로 사용 설정"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"위치 및 시간대에 맞게 야간 모드로 전환"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"야간 모드 사용 중일 때"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android OS용 어두운 테마 사용"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"농담 효과 조정"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"밝기 조정"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"설정 등 밝은 테마에서 일반적으로 표시되는 Android OS의 핵심 영역에 어두운 테마가 적용됩니다."</string>
- <string name="color_apply" msgid="9212602012641034283">"적용"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"설정 확인"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"일부 색상 설정으로 인해 이 기기를 사용하지 못할 수 있습니다. 확인을 클릭하여 이러한 색상 설정을 확인하지 않으면 10초 후에 설정이 초기화됩니다."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"배터리 사용량"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"충전하는 동안 배터리 세이버는 사용할 수 없습니다."</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"배터리 세이버"</string>
diff --git a/packages/SystemUI/res/values-ky-rKG/config.xml b/packages/SystemUI/res/values-ky-rKG/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-ky-rKG/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index 46fed8b..7645b93 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Роуминг"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> чектөө"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> эскертүү"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Иштөө режими"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Түнкү жарык"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Түнкү жарык күйүк, өчүрүү үчүн таптап коюңуз"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Түнкү жарык өчүк, күйгүзүү үчүн таптап коюңуз"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Акыркы колдонмолор жок"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Баарын тазаладыңыз"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Колдонмо жөнүндө маалымат"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Туурасынан бөлүү"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Тигинен бөлүү"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Ыңгайлаштырылган бөлүү"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Кубатталды"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Кубатталууда"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> толгонго чейин"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> үндү катуулатуу диалогу"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Үндүн баштапкы деңгээлин калыбына келтирүү үчүн таптап коюңуз."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Жумуш профилиңизди колдонуп жатасыз"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Чалуу"</item>
+ <item msgid="5997713001067658559">"Тутум"</item>
+ <item msgid="7858983209929864160">"Шыңгыратуу"</item>
+ <item msgid="1850038478268896762">"Мультимедия"</item>
+ <item msgid="8265110906352372092">"Ойготкуч"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Үнүн чыгаруу үчүн таптап коюңуз."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Дирилдөөгө коюу үчүн таптап коюңуз. Атайын мүмкүнчүлүктөр кызматынын үнүн өчүрүп койсо болот."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Үнүн өчүрүү үчүн таптап коюңуз. Атайын мүмкүнчүлүктөр кызматынын үнүн өчүрүп койсо болот."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Дагы жөндөөлөр"</string>
<string name="notification_done" msgid="5279426047273930175">"Бүттү"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> эскертмесин башкаруу каражаттары"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Түсү жана көрүнүшү"</string>
- <string name="night_mode" msgid="3540405868248625488">"Түнкү режим"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Дисплейди калибрлөө"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Күйүк"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Өчүк"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Автоматтык түрдө күйгүзүү"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Жайгашкан жерге жана убакытка жараша түнкү режимге которулуңуз"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Түнкү режим күйүп турганда"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android OS үчүн караңгы тема колдонуу"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Кошумча түсүн тууралоо"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Жарыктыгын тууралоо"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Адатта жарык темада көрсөтүлүүчү Android тутумунун негизги элементтерине (Жөндөөлөр сыяктуу) колдонула турган караңгы тема."</string>
- <string name="color_apply" msgid="9212602012641034283">"Колдонуу"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Жөндөөлөрдү ырастоо"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Айрым түс жөндөөлөрү бул түзмөктү колдонулгус кылып коюшу мүмкүн. Бул түс жөндөөлөрүн ырастоо үчүн OK баскычын чыкылдатыңыз, болбосо бул жөндөөлөр 10 секунддан кийин баштапкы абалына келтирилет."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Батарея колдонулушу"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Батареяны үнөмдөгүч түзмөк кубатталып жатканда иштебейт"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Батареяны үнөмдөгүч"</string>
diff --git a/packages/SystemUI/res/layout/night_mode_settings.xml b/packages/SystemUI/res/values-ldrtl/config.xml
similarity index 71%
rename from packages/SystemUI/res/layout/night_mode_settings.xml
rename to packages/SystemUI/res/values-ldrtl/config.xml
index 3725e78..40604c1 100644
--- a/packages/SystemUI/res/layout/night_mode_settings.xml
+++ b/packages/SystemUI/res/values-ldrtl/config.xml
@@ -14,11 +14,8 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <include layout="@layout/switch_bar" />
-
-</LinearLayout>
+<resources>
+ <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
+ when the PIP menu is shown with settings. -->
+ <string translatable="false" name="pip_settings_bounds">"778 54 1258 324"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-lo-rLA/config.xml b/packages/SystemUI/res/values-lo-rLA/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-lo-rLA/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index f8aea26..6b1fca8 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"ໂຣມມິງ"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"ຈຳກັດ <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"ຄຳເຕືອນ <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"ໂໝດການເຮັດວຽກ"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"ແສງກາງຄືນ"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"ເປີດແສງກາງຄືນຢູ່, ແຕະເພື່ອປິດໄວ້"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"ປິດແສງກາງຄືນຢູ່, ແຕະເພື່ອເປີດໃຊ້"</string>
<string name="recents_empty_message" msgid="808480104164008572">"ບໍ່ມີລາຍການຫຼ້າສຸດ"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"ທ່ານລຶບລ້າງທຸກຢ່າງແລ້ວ"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"ຂໍ້ມູນແອັບພລິເຄຊັນ"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ການແຍກລວງຂວາງ"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ການແຍກລວງຕັ້ງ"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ການແຍກກຳນົດເອງ"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"ສາກເຕັມແລ້ວ."</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"ກຳລັງສາກໄຟ"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> ຈຶ່ງຈະເຕັມ"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ແມ່ນໜ້າຕ່າງລະດັບສຽງ"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"ແຕະເພື່ອກູ້ຕົ້ນສະບັບຄືນມາ."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"ທ່ານກຳລັງໃຊ້ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກຂອງທ່ານ"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"ໂທ"</item>
+ <item msgid="5997713001067658559">"ລະບົບ"</item>
+ <item msgid="7858983209929864160">"ເຕືອນດ້ວຍສຽງ"</item>
+ <item msgid="1850038478268896762">"ມີເດຍ"</item>
+ <item msgid="8265110906352372092">"ໂມງປຸກ"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. ແຕະເພື່ອເຊົາປິດສຽງ."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. ແຕະເພື່ອຕັ້ງເປັນສັ່ນ. ບໍລິການຊ່ວຍເຂົ້າເຖິງອາດຖືກປິດສຽງໄວ້."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. ແຕະເພື່ອປິດສຽງ. ບໍລິການຊ່ວຍເຂົ້າເຖິງອາດຖືກປິດສຽງໄວ້."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"ການຕັ້ງຄ່າເພີ່ມເຕີມ"</string>
<string name="notification_done" msgid="5279426047273930175">"ສຳເລັດແລ້ວ"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"ການຄວບຄຸມການແຈ້ງເຕືອນ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"ສີ ແລະ ລັກສະນະ"</string>
- <string name="night_mode" msgid="3540405868248625488">"ໂໝດກາງຄືນ"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"ປັບໜ້າຈໍ"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"ເປີດ"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"ປິດ"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"ເປີດໃຊ້ອັດຕະໂນມັດ"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"ສະລັບໄປໃຊ້ໂໝດກາງຄືນຕາມຄວາມເໝາະສົມກັບສະຖານທີ່ ແລະ ເວລາໃນແຕ່ລະມື້"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"ເມື່ອເປີດໂໝດກາງຄືນ"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"ໃຊ້ຮູບແບບສີສັນແບບມືດສຳລັບ Android OS"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"ປັບແຕ່ງໂທນສີ"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"ປັບແຕ່ງຄວາມສະຫວ່າງ"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"ຮູບແບບສີສັນມືດແມ່ນນຳໃຊ້ກັບພື້ນທີ່ຫຼັກຂອງ Android OS ທີ່ປົກກະຕິຈະສະແດງເປັນຮູບແບບສີສັນແຈ້ງ ເຊັ່ນ: ການຕັ້ງຄ່າ."</string>
- <string name="color_apply" msgid="9212602012641034283">"ນຳໃຊ້"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"ຢືນຢັນການຕັ້ງຄ່າ"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"ບາງການຕັ້ງຄ່າສີສາມາດເຮັດໃຫ້ອຸປະກອນນີ້ບໍ່ສາມາດໃຊ້ໄດ້. ຄລິກ ຕົກລົງ ເພື່ອຢືນຢັນການຕັ້ງຄ່າສີເຫຼົ່ານີ້, ຖ້າບໍ່ດັ່ງນັ້ນ ການຕັ້ງຄ່າເຫຼົ່ານີ້ຈະຕັ້ງຄືນໃໝ່ ຫຼັງຈາກ 10 ວິນາທີ."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"ການໃຊ້ແບັດເຕີຣີ"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ຕົວປະຢັດແບັດເຕີຣີບໍ່ມີໃຫ້ນຳໃຊ້ໃນລະຫວ່າງການສາກ"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"ຕົວປະຢັດແບັດເຕີຣີ"</string>
diff --git a/packages/SystemUI/res/values-lt/config.xml b/packages/SystemUI/res/values-lt/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-lt/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index a40bef9..46e50fb 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -145,7 +145,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Tarptinklinis ryšys"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Kraštas"</string>
@@ -321,6 +323,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Limitas: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> įspėjimas"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Darbo režimas"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Nakties šviesa"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Nakties šviesa įjungta. Palieskite, kad išjungtumėte"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Nakties šviesa išjungta. Palieskite, kad įjungtumėte"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Nėra jokių naujausių elementų"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Viską išvalėte"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Programos informacija"</string>
@@ -334,6 +339,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Horizontalus skaidymas"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikalus skaidymas"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Tinkintas skaidymas"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Įkrautas"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Kraunamas"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> iki visiško įkrovimo"</string>
@@ -438,6 +445,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ yra garsumo valdymo dialogo langas"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Palieskite, kad atkurtumėte originalą."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Naudojate darbo profilį"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Skambinti"</item>
+ <item msgid="5997713001067658559">"Sistema"</item>
+ <item msgid="7858983209929864160">"Skambinti"</item>
+ <item msgid="1850038478268896762">"Medija"</item>
+ <item msgid="8265110906352372092">"Signalas"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Palieskite, kad įjungtumėte garsą."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Palieskite, kad nustatytumėte vibravimą. Gali būti nutildytos pritaikymo neįgaliesiems paslaugos."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Palieskite, kad nutildytumėte. Gali būti nutildytos pritaikymo neįgaliesiems paslaugos."</string>
@@ -508,21 +527,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Daugiau nustatymų"</string>
<string name="notification_done" msgid="5279426047273930175">"Atlikta"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ pranešimų valdikliai"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Spalva ir išvaizda"</string>
- <string name="night_mode" msgid="3540405868248625488">"Naktinis režimas"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Kalibruoti ekraną"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Įjungta"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Išjungta"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Įjungti automatiškai"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Perjungti į naktinį režimą pagal vietovę ir dienos laiką"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Kai įjungtas naktinis režimas"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Naudoti tamsią „Android“ OS temą"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Koreguoti atspalvį"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Koreguoti šviesumą"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Pagrindinėms „Android“ OS dalims, kurioms paprastai taikoma šviesi tema, pvz., skilčiai „Nustatymai“, bus pritaikyta tamsi tema."</string>
- <string name="color_apply" msgid="9212602012641034283">"Taikyti"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Nustatymų patvirtinimas"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Dėl kai kurių spalvų nustatymų įrenginys gali būti netinkamas naudoti. Spustelėkite „Gerai“, kad patvirtintumėte šiuos spalvų nustatymus. Kitaip šie nustatymai bus nustatyti iš naujo po 10 sekundžių."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Akum. energ. vartoj."</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Akumuliatoriaus tausojimo priemonė nepasiekiama įkraunant"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Akumuliatoriaus tausojimo priemonė"</string>
diff --git a/packages/SystemUI/res/values-lv/config.xml b/packages/SystemUI/res/values-lv/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-lv/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index d5b29c4..9efad2e 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -144,7 +144,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Viesabonēšana"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"EDGE"</string>
@@ -319,6 +321,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Ierobežojums: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> brīdinājums"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Darba režīms"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Nakts režīms"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Nakts režīms ir ieslēgts. Pieskarieties, lai to izslēgtu."</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Nakts režīms ir izslēgts. Pieskarieties, lai to ieslēgtu."</string>
<string name="recents_empty_message" msgid="808480104164008572">"Nav nesenu vienumu"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Visi uzdevumi ir notīrīti"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informācija par lietojumprogrammu"</string>
@@ -332,6 +337,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Horizontāls dalījums"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikāls dalījums"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Pielāgots dalījums"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Akumulators uzlādēts"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Notiek uzlāde"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> līdz pilnam akumulatoram"</string>
@@ -436,6 +443,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ir skaļuma dialoglodziņš"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Pieskarieties, lai atjaunotu sākotnējo saturu."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Jūs izmantojat darba profilu."</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Zvans"</item>
+ <item msgid="5997713001067658559">"Sistēma"</item>
+ <item msgid="7858983209929864160">"Zvanīt"</item>
+ <item msgid="1850038478268896762">"Multivide"</item>
+ <item msgid="8265110906352372092">"Signāls"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Pieskarieties, lai ieslēgtu skaņu."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Pieskarieties, lai iestatītu uz vibrozvanu. Var tikt izslēgti pieejamības pakalpojumu signāli."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Pieskarieties, lai izslēgtu skaņu. Var tikt izslēgti pieejamības pakalpojumu signāli."</string>
@@ -506,21 +525,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Citi iestatījumi"</string>
<string name="notification_done" msgid="5279426047273930175">"Gatavs"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> paziņojumu vadīklas"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Krāsas un izskats"</string>
- <string name="night_mode" msgid="3540405868248625488">"Nakts režīms"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Ekrāna kalibrēšana"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Ieslēgts"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Izslēgts"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Ieslēgt automātiski"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Pārslēgt uz nakts režīmu atbilstoši atrašanās vietai un diennakts laikam"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Ja ir ieslēgts nakts režīms"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Izmantot tumšo motīvu operētājsistēmai Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Regulēt toni"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Regulēt spilgtumu"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Tumšais motīvs tiek lietots galvenajos operētājsistēmas Android elementos, kas parasti tiek rādīti ar gaišu motīvu, piemēram, lietotnē Iestatījumi."</string>
- <string name="color_apply" msgid="9212602012641034283">"Lietot"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Iestatījumu apstiprināšana"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Noteiktu krāsu iestatījumu dēļ šī ierīce var kļūt nelietojama. Lai apstiprinātu šos krāsu iestatījumus, noklikšķiniet uz Labi. Ja to neizdarīsiet, pēc 10 sekundēm šie iestatījumi tiks atiestatīti."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Akumulatora lietojums"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Akumulatora jaudas taupīšanas režīms uzlādes laikā nav pieejams."</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Akumulatora jaudas taupīšanas režīms"</string>
diff --git a/packages/SystemUI/res/values-mk-rMK/config.xml b/packages/SystemUI/res/values-mk-rMK/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-mk-rMK/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml
index f4dd7bc..549543d 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings.xml
@@ -85,7 +85,7 @@
<string name="accessibility_menu" msgid="316839303324695949">"Мени"</string>
<string name="accessibility_recent" msgid="5208608566793607626">"Краток преглед"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Пребарај"</string>
- <string name="accessibility_camera_button" msgid="8064671582820358152">"Фотоапарат"</string>
+ <string name="accessibility_camera_button" msgid="8064671582820358152">"Камера"</string>
<string name="accessibility_phone_button" msgid="6738112589538563574">"Телефон"</string>
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"Гласовна помош"</string>
<string name="accessibility_unlock_button" msgid="128158454631118828">"Отклучување"</string>
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Роаминг"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Лимит: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Предупредување за <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Режим на работа"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Ноќно светло"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Ноќното светло е вклучено, допрете за да се исклучи"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Ноќното светло е исклучено, допрете за да се вклучи"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Нема неодамнешни ставки"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Исчистивте сѐ"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Информации за апликацијата"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Раздели хоризонтално"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Раздели вертикално"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Раздели прилагодено"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Наполнета"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Се полни"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> додека не се наполни"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> е дијалог за јачина на звук"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Допрете за да го вратите оригиналот."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Го користите работниот профил"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Повикај"</item>
+ <item msgid="5997713001067658559">"Систем"</item>
+ <item msgid="7858983209929864160">"Ѕвони"</item>
+ <item msgid="1850038478268896762">"Аудио-визуелни содржини"</item>
+ <item msgid="8265110906352372092">"Аларм"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Допрете за да вклучите звук."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Допрете за да поставите на вибрации. Можеби ќе се исклучи звукот на услугите за достапност."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Допрете за да исклучите звук. Можеби ќе се исклучи звукот на услугите за достапност."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Повеќе поставки"</string>
<string name="notification_done" msgid="5279426047273930175">"Готово"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Контроли за известувања на <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Боја и изглед"</string>
- <string name="night_mode" msgid="3540405868248625488">"Ноќен режим"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Калибрирај го екранот"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Вклучено"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Исклучено"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Вклучи автоматски"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Префрли во Ноќен режим како што е соодветно за локацијата и времето во денот"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Кога Ноќниот режим е вклучен"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Користете ја темната тема за ОС Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Приспособи ја бојата"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Приспособи ја осветленоста"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Темната тема се применува на основните области на Android OS што обично се прикажуваат во светла тема, како Поставки."</string>
- <string name="color_apply" msgid="9212602012641034283">"Примени"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Потврдете ги поставките"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Некои поставки на боите може да го направат уредот неупотреблив. Кликнете на Во ред за да ги потврдите овие поставки на боите, инаку тие поставки ќе се ресетираат по 10 секунди."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Користење батерија"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Штедачот на батерија не е достапен при полнење"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Штедач на батерија"</string>
diff --git a/packages/SystemUI/res/values-ml-rIN/config.xml b/packages/SystemUI/res/values-ml-rIN/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-ml-rIN/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml
index 7178b29..9f596b8 100644
--- a/packages/SystemUI/res/values-ml-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ml-rIN/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"റോമിംഗ്"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> പരിധി"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> മുന്നറിയിപ്പ്"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"പ്രവർത്തന മോഡ്"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"നൈറ്റ് ലൈറ്റ്"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"നൈറ്റ് ലൈറ്റ് ഓണാണ്, ഓഫാക്കുന്നതിന് ടാപ്പുചെയ്യുക"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"നൈറ്റ് ലൈറ്റ് ഓഫാണ്, ഓണാക്കുന്നതിന് ടാപ്പുചെയ്യുക"</string>
<string name="recents_empty_message" msgid="808480104164008572">"സമീപകാല ഇനങ്ങൾ ഒന്നുമില്ല"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"നിങ്ങൾ എല്ലാം മായ്ച്ചിരിക്കുന്നു"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"ആപ്പ് വിവരം"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"തിരശ്ചീനമായി വേർതിരിക്കുക"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ലംബമായി വേർതിരിക്കുക"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ഇഷ്ടാനുസൃതമായി വേർതിരിക്കുക"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"ചാർജായി"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"ചാർജ്ജുചെയ്യുന്നു"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"പൂർണ്ണമായും ചാർജ്ജാകുന്നതിന്, <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g>, വോളിയം ഡയലോഗാണ്"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"ഒറിജിനൽ പുനഃസ്ഥാപിക്കാൻ ടാപ്പുചെയ്യുക."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"നിങ്ങൾ ഉപയോഗിക്കുന്നത് ഔദ്യോഗിക പ്രൊഫൈലാണ്"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"വിളിക്കുക"</item>
+ <item msgid="5997713001067658559">"സിസ്റ്റം"</item>
+ <item msgid="7858983209929864160">"റിംഗുചെയ്യുക"</item>
+ <item msgid="1850038478268896762">"മീഡിയ"</item>
+ <item msgid="8265110906352372092">"അലാറം"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"ബ്ലൂടൂത്ത്"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. അൺമ്യൂട്ടുചെയ്യുന്നതിന് ടാപ്പുചെയ്യുക."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. വൈബ്രേറ്റിലേക്ക് സജ്ജമാക്കുന്നതിന് ടാപ്പുചെയ്യുക. പ്രവേശനക്ഷമതാ സേവനങ്ങൾ മ്യൂട്ടുചെയ്യപ്പെട്ടേക്കാം."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. മ്യൂട്ടുചെയ്യുന്നതിന് ടാപ്പുചെയ്യുക. പ്രവേശനക്ഷമതാ സേവനങ്ങൾ മ്യൂട്ടുചെയ്യപ്പെട്ടേക്കാം."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"കൂടുതൽ ക്രമീകരണം"</string>
<string name="notification_done" msgid="5279426047273930175">"പൂർത്തിയായി"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> അറിയിപ്പ് നിയന്ത്രണങ്ങൾ"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"വർണ്ണവും രൂപഭാവവും"</string>
- <string name="night_mode" msgid="3540405868248625488">"നൈറ്റ് മോഡ്"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"ഡിസ്പ്ലേ കാലിബ്രേറ്റുചെയ്യുക"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"ഓൺ"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"ഓഫ്"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"സ്വയമേവ ഓണാക്കുക"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"ലൊക്കേഷനും ദിവസത്തിലെ സമയത്തിനും അനുയോജ്യമായ തരത്തിൽ നൈറ്റ് മോഡിലേക്ക് സ്വിച്ചുചെയ്യുക"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"നൈറ്റ് മോഡ് ഓണായിരിക്കുമ്പോൾ"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android OS-നുള്ള ഇരുണ്ട തീം ഉപയോഗിക്കുക"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"ടിന്റ് ക്രമപ്പെടുത്തുക"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"തെളിച്ചം ക്രമപ്പെടുത്തുക"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"ക്രമീകരണം പോലെയുള്ള, ഒരു ലൈറ്റ് തീമിൽ സാധാരണ ഗതിയിൽ പ്രദർശിപ്പിക്കപ്പെടുന്ന Android OS-ന്റെ അടിസ്ഥാന ഇടങ്ങളിലേക്ക്, ഇരുണ്ട തീം പ്രയോഗിക്കുന്നു."</string>
- <string name="color_apply" msgid="9212602012641034283">"ബാധകമാക്കുക"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"ക്രമീകരണം സ്ഥിരീകരിക്കുക"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"ചില വർണ്ണ ക്രമീകരണത്തിന് ഈ ഉപകരണത്തെ ഉപയോഗരഹിതമാക്കാനാകും. ഈ വർണ്ണ ക്രമീകരണം സ്ഥിരീകരിക്കുന്നതിന് ശരി എന്നതിൽ ക്ലിക്കുചെയ്യുക, അല്ലെങ്കിൽ 10 സെക്കൻഡിന് ശേഷം ഈ ക്രമീകരണം പുനഃക്രമീകരിക്കപ്പെടും."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"ബാറ്ററി ഉപയോഗം"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ചാർജുചെയ്യുന്ന സമയത്ത് ബാറ്ററി സേവർ ലഭ്യമല്ല"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"ബാറ്ററി സേവർ"</string>
diff --git a/packages/SystemUI/res/values-mn-rMN/config.xml b/packages/SystemUI/res/values-mn-rMN/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-mn-rMN/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index 296658b..f3a2f08 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -141,7 +141,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Рүүминг"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -315,6 +317,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> хязгаар"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> анхааруулга"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Ажлын горим"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Шөнийн гэрэл"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Шөнийн гэрэл асаалттай байна. Унтраахын тулд товшино уу"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Шөнийн гэрэл унтраалттай байна. Асаахын тулд товшино уу"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Сүүлийн үеийн зүйл байхгүй"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Та бүгдийг нь устгасан"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Аппликешны мэдээлэл"</string>
@@ -328,6 +333,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Хэвтээ чиглэлд хуваах"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Босоо чиглэлд хуваах"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Хүссэн хэлбэрээр хуваах"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Цэнэглэгдсэн"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Цэнэглэж байна"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"дүүргэхэд <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
@@ -432,6 +439,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> нь дууны диалог юм."</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Эх хувилбарыг сэргээхийн тулд дарна уу."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Та өөрийн ажлын профайлыг ашиглаж байна"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Дуудлага"</item>
+ <item msgid="5997713001067658559">"Систем"</item>
+ <item msgid="7858983209929864160">"Хонх дуугаргах"</item>
+ <item msgid="1850038478268896762">"Медиа"</item>
+ <item msgid="8265110906352372092">"Сэрүүлэг"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Дууг нь нээхийн тулд товшино уу."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Чичиргээнд тохируулахын тулд товшино уу. Хүртээмжийн үйлчилгээний дууг хаасан."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Дууг нь хаахын тулд товшино уу. Хүртээмжийн үйлчилгээний дууг хаасан."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Бусад тохиргоо"</string>
<string name="notification_done" msgid="5279426047273930175">"Дууссан"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> мэдэгдлийн хяналт"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Өнгө, харагдах байдал"</string>
- <string name="night_mode" msgid="3540405868248625488">"Шөнийн горим"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Дэлгэцийг тохируулах"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Идэвхтэй"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Идэвхгүй"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Автоматаар асаах"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Тухайн өдрийн байршил, цагийн тохиромжтой үед Шөнийн горимд шилжүүлэх"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Шөнийн горим идэвхтэй үед"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android-н үйлдлийн системд бараан загварыг ашиглах"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Өнгөний нягтаршилыг тохируулах"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Гэрэлтүүлгийг тохируулах"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Тохиргоо зэрэг тогтмол цайвар загварт харуулдаг Android үйлдлийн системийн гол хэсгийг бараан загварт харуулна."</string>
- <string name="color_apply" msgid="9212602012641034283">"Хэрэгжүүлэх"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Тохиргоог баталгаажуулах"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Зарим өнгөний тохиргоо энэ төхөөрөмжийг ашиглах боломжгүй болгож болзошгүй. OK товчлуурыг дарж эдгээр өнгөний тохиргоог зөвшөөрөхгүй бол энэ тохиргоо нь 10 секундын дараа шинэчлэгдэх болно."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Тэжээл ашиглалт"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Цэнэглэх үед тэжээл хэмнэгч ажиллахгүй"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Тэжээл хэмнэгч"</string>
diff --git a/packages/SystemUI/res/values-mr-rIN/config.xml b/packages/SystemUI/res/values-mr-rIN/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-mr-rIN/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml
index 1fb4beb..e5c6410 100644
--- a/packages/SystemUI/res/values-mr-rIN/strings.xml
+++ b/packages/SystemUI/res/values-mr-rIN/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"रोमिंग"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -295,7 +297,7 @@
<string name="quick_settings_cast_title" msgid="7709016546426454729">"कास्ट करा"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"कास्ट करत आहे"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"निनावी डिव्हाइस"</string>
- <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"कास्ट करण्यास सज्ज"</string>
+ <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"कास्ट करण्यास तयार"</string>
<string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"कोणतेही डिव्हाइसेस उपलब्ध नाहीत"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"चमक"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"स्वयंचलित"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> मर्यादा"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> चेतावणी"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"कार्य मोड"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"रात्रीचा प्रकाश"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"रात्रीचा प्रकाश चालू आहे, बंद करण्यासाठी टॅप करा"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"रात्रीचा प्रकाश बंद आहे, चालू करण्यासाठी टॅप करा"</string>
<string name="recents_empty_message" msgid="808480104164008572">"अलीकडील कोणतेही आयटम नाहीत"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"आपण सर्वकाही साफ केले"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"अनुप्रयोग माहिती"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"क्षैतिज विभाजित करा"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"अनुलंब विभाजित करा"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"सानुकूल विभाजित करा"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"चार्ज झाली"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"चार्ज होत आहे"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> पूर्ण होईपर्यंत"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> हा व्हॉल्यूम संवाद आहे"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"मूळ पुनर्संचयित करण्यासाठी टॅप करा."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"आपण आपले कार्य प्रोफाईल वापरत आहात"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"कॉल करा"</item>
+ <item msgid="5997713001067658559">"सिस्टीम"</item>
+ <item msgid="7858983209929864160">"रिंग करा"</item>
+ <item msgid="1850038478268896762">"मीडिया"</item>
+ <item msgid="8265110906352372092">"अलार्म"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"ब्लूटुथ"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. सशब्द करण्यासाठी टॅप करा."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. कंपन सेट करण्यासाठी टॅप करा. प्रवेशयोग्यता सेवा नि:शब्द केल्या जाऊ शकतात."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. नि:शब्द करण्यासाठी टॅप करा. प्रवेशक्षमता सेवा नि:शब्द केल्या जाऊ शकतात."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"अधिक सेटिंग्ज"</string>
<string name="notification_done" msgid="5279426047273930175">"पूर्ण झाले"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> सूचना नियंत्रणे"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"रंग आणि स्वरूप"</string>
- <string name="night_mode" msgid="3540405868248625488">"रात्र मोड"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"प्रदर्शनाचे मापन करा"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"चालू"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"बंद"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"स्वयंचलितपणे चालू करा"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"स्थान आणि दिवसाच्या वेळेसाठी योग्य असल्यानुसार रात्र मोड मध्ये स्विच करा"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"रात्र मोड चालू असताना"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android OS साठी गडद थीमचा वापर करा"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"रंगाची छटा समायोजित करा"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"चकाकी समायोजित करा"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"सेटिंग्ज सारख्या प्रकाश थीममध्ये प्रदर्शित केल्या जाणाऱ्या Android OS च्या मुख्य क्षेत्रांवर गडद थीम लागू केली जाते."</string>
- <string name="color_apply" msgid="9212602012641034283">"लागू करा"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"सेटिंग्जची पुष्टी करा"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"काही रंग सेटिंग्ज या डिव्हाइसला निरुपयोगी करू शकतात. या रंग सेटिंग्जची पुष्टी करण्यासाठी ठीक आहे दाबा अन्यथा या सेटिंग्ज 10 सेकंदांनंतर रीसेट होतील."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"बॅटरी वापर"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"चार्ज करताना बॅटरी बचतकर्ता उपलब्ध नाही"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"बॅटरी बचतकर्ता"</string>
diff --git a/packages/SystemUI/res/values-ms-rMY/config.xml b/packages/SystemUI/res/values-ms-rMY/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-ms-rMY/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 8729ae8..7c543a6 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Perayauan"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> had"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Amaran <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Mod kerja"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Cahaya Malam"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Cahaya Malam dihidupkan, ketik untuk mematikannya"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Cahaya Malam dimatikan, ketik untuk menghidupkannya"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Tiada item terbaharu"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Anda telah mengetepikan semua item"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Maklumat Aplikasi"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Mendatar Terpisah"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Menegak Terpisah"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Tersuai Terpisah"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Sudah dicas"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Mengecas"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Lagi <xliff:g id="CHARGING_TIME">%s</xliff:g> untuk penuh"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ialah dialog kelantangan"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Ketik untuk memulihkan yang asal."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Anda sedang menggunakan profil kerja"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Panggil"</item>
+ <item msgid="5997713001067658559">"Sistem"</item>
+ <item msgid="7858983209929864160">"Dering"</item>
+ <item msgid="1850038478268896762">"Media"</item>
+ <item msgid="8265110906352372092">"Penggera"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Ketik untuk menyahredam."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Ketik untuk menetapkan pada getar. Perkhidmatan kebolehaksesan mungkin diredamkan."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Ketik untuk meredam. Perkhidmatan kebolehaksesan mungkin diredamkan."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Lagi tetapan"</string>
<string name="notification_done" msgid="5279426047273930175">"Selesai"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Kawalan pemberitahuan <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Warna dan penampilan"</string>
- <string name="night_mode" msgid="3540405868248625488">"Mod malam"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Tentukur paparan"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Hidup"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Mati"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Hidupkan secara automatik"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Beralih ke Mod Malam sebagaimana sesuai untuk lokasi dan masa"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Apabila Mod Malam dihidupkan"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Gunakan tema gelap untuk OS Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Laraskan seri warna"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Laraskan kecerahan"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Tema gelap digunakan pada bahagian teras OS Android yang biasanya dipaparkan dalam tema cerah, seperti Tetapan."</string>
- <string name="color_apply" msgid="9212602012641034283">"Gunakan"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Sahkan tetapan"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Sesetengah tetapan warna boleh menjadikan peranti ini tidak dapat digunakan. Klik OK untuk mengesahkan tetapan warna ini, jika tidak, tetapan ini akan ditetapkan semula selepas 10 saat."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Penggunaan bateri"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Penjimat Bateri tidak tersedia semasa mengecas"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Penjimat Bateri"</string>
diff --git a/packages/SystemUI/res/values-my-rMM/config.xml b/packages/SystemUI/res/values-my-rMM/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-my-rMM/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index 2522e39..56a7249 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -20,11 +20,11 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7164937344850004466">"စနစ်၏UI"</string>
- <string name="status_bar_clear_all_button" msgid="7774721344716731603">"ဖယ်ရှားရန်"</string>
+ <string name="status_bar_clear_all_button" msgid="7774721344716731603">"ရှင်းရန်"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"စာရင်းမှ ဖယ်မည်"</string>
- <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"အပ်ပလီကေးရှင်း အချက်အလက်များ"</string>
+ <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"အက်ပ်အချက်အလက်များ"</string>
<string name="status_bar_no_recent_apps" msgid="7374907845131203189">"သင်၏ မကြာမီက မျက်နှာပြင်များ ဒီမှာ ပေါ်လာကြမည်"</string>
- <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"လတ်တလောအပ်ပလီကေးရှင်းများအား ဖယ်ထုတ်မည်"</string>
+ <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"လတ်တလောအက်ပ်များအား ပယ်ရန်"</string>
<plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
<item quantity="other">ခြုံကြည့်မှုထဲမှ မျက်နှာပြင် %d ခု</item>
<item quantity="one">ခြုံကြည့်မှုထဲမှ မျက်နှာပြင် 1 ခု</item>
@@ -65,7 +65,7 @@
<string name="usb_debugging_always" msgid="303335496705863070">"ဒီကွန်ပျူတာမှ အမြဲခွင့်ပြုရန်"</string>
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB အမှားပြင်ဆင်ခြင်း ခွင့်မပြုပါ"</string>
<string name="usb_debugging_secondary_user_message" msgid="8572228137833020196">"ဤစက်ပစ္စည်းသို့ လက်ရှိဝင်ရောက်ထားသည့် အသုံးပြုသူသည် USB အမှားပြင်ဆင်ခြင်း ဖွင့်၍မရပါ။ ဤအင်္ဂါရပ်ကို အသုံးပြုရန်၊ ကျေးဇူးပြု၍ ကြီးကြပ်သူသို့ပြောင်းပါ။"</string>
- <string name="compat_mode_on" msgid="6623839244840638213">"ဖန်သားပြင်ပြည့် ချဲ့ခြင်း"</string>
+ <string name="compat_mode_on" msgid="6623839244840638213">"ဇူးမ်အပြည့်ဆွဲခြင်း"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"ဖန်သားပြင်အပြည့်ဆန့်ခြင်း"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"ဖန်သားပြင်ဓါတ်ပုံသိမ်းစဉ်.."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား သိမ်းဆည်းပါမည်"</string>
@@ -96,9 +96,9 @@
<string name="voice_assist_label" msgid="3956854378310019854">"အသံ အကူအညီအား ဖွင့်ရန်"</string>
<string name="camera_label" msgid="7261107956054836961">"ကင်မရာ ဖွင့်ရန်"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"အလုပ်သစ်စီစဥ်မှုကို ရွေးပါ။"</string>
- <string name="cancel" msgid="6442560571259935130">"မလုပ်တော့ပါ"</string>
- <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"အံ့ဝင်သောချုံ့ချဲ့ခလုတ်"</string>
- <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ဖန်သားပြင်ပေါ်တွင် အသေးမှအကြီးသို့ချဲ့ခြင်း"</string>
+ <string name="cancel" msgid="6442560571259935130">"မလုပ်တော့"</string>
+ <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"အံဝင်ခွင်ကျ ဇူးမ်ခလုတ်"</string>
+ <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ဖန်သားပြင်ပေါ်တွင် အသေးမှအကြီးသို့ ဇူးမ်ဆွဲခြင်း"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ဘလူးတုသ်ချိတ်ဆက်ထားမှု"</string>
<string name="accessibility_bluetooth_disconnected" msgid="7416648669976870175">"ဘလူးတုသ်ချိတ်ဆက်မှုပြတ်တောက်သည်"</string>
<string name="accessibility_no_battery" msgid="358343022352820946">"ဘက်ထရီမရှိပါ။"</string>
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"မြန်နှုန်းမြင့်လိုင်း"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"ကွန်ယက်ပြင်ပဒေတာအသုံးပြုခြင်း"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"EDGE"</string>
@@ -170,10 +172,10 @@
<!-- no translation found for accessibility_casting (6887382141726543668) -->
<skip />
<string name="accessibility_work_mode" msgid="2478631941714607225">"အလုပ် မုဒ်"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>ကို ပယ်လိုက်ရန်"</string>
+ <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> ကို ပယ်ရန်"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ထုတ်ထားသည်။"</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"မကြာသေးမီက အပလီကေးရှင်းများအားလုံး ဖယ်ထုတ်ပြီးပါပြီ။"</string>
- <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> အက်ပ်အချက်အလက်ကို ဖွင့်ပါ။"</string>
+ <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> အပလီကေးရှင်းအချက်အလက်ကို ဖွင့်ပါ။"</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>ကို စတင်နေသည်။"</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g><xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"အကြောင်းကြားချက်ကိုဖယ်ရှားပြီး"</string>
@@ -317,9 +319,12 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ကန့်သတ်ချက်"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> သတိပေးချက်"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"အလုပ် မုဒ်"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"ညအလင်းရောင်"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"ညအလင်းရောင်ကို ဖွင့်ထားသည်၊ ပိတ်ရန်တို့ပါ"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"ညအလင်းရောင်ကို ပိတ်ထားသည်၊ ဖွင့်ရန်တို့ပါ"</string>
<string name="recents_empty_message" msgid="808480104164008572">"မကြာမီကဖွင့်ထားသည်များ မရှိပါ"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"သင်အားလုံးကို ရှင်းလင်းပြီးပါပြီ"</string>
- <string name="recents_app_info_button_label" msgid="2890317189376000030">"အက်ပ် အင်ဖို"</string>
+ <string name="recents_app_info_button_label" msgid="2890317189376000030">"အပလီကေးရှင်းအင်ဖို"</string>
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"မျက်နှာပြင် ပင်ထိုးမှု"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"ရှာဖွေရန်"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ကို မစနိုင်ပါ။"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ရေပြင်ညီ ပိုင်းမည်"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ဒေါင်လိုက်ပိုင်းမည်"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"စိတ်ကြိုက် ပိုင်းမည်"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"အားသွင်းပြီး"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"အားသွင်းနေ"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> ပြည်သည့် အထိ"</string>
@@ -374,9 +381,9 @@
<string name="guest_wipe_session_title" msgid="6419439912885956132">"ပြန်လာတာ ကြိုဆိုပါသည်၊ ဧည့်သည်!"</string>
<string name="guest_wipe_session_message" msgid="8476238178270112811">"သင်သည် သင်၏ ချိတ်ဆက်မှုကို ဆက်ပြုလုပ် လိုပါသလား?"</string>
<string name="guest_wipe_session_wipe" msgid="5065558566939858884">"အစမှ ပြန်စပါ"</string>
- <string name="guest_wipe_session_dontwipe" msgid="1401113462524894716">"ဟုတ်ကဲ့၊ ဆက်လုပ်ပါ"</string>
+ <string name="guest_wipe_session_dontwipe" msgid="1401113462524894716">"ဆက်လုပ်ပါ"</string>
<string name="guest_notification_title" msgid="1585278533840603063">"ဧည့်သည် အသုံးပြုသူ"</string>
- <string name="guest_notification_text" msgid="335747957734796689">"App များနှင့် ဒေတာအား ဖျက်ရန်၊ တခဏသုံးစွဲသူအား ဖယ်ရှားပါ"</string>
+ <string name="guest_notification_text" msgid="335747957734796689">"အက်ပ်များနှင့် ဒေတာအား ဖျက်ရန်၊ တခဏသုံးစွဲသူအား ဖယ်ရှားပါ"</string>
<string name="guest_notification_remove_action" msgid="8820670703892101990">"ဧည့်သည်ကို ဖယ်ထုတ်မည်"</string>
<string name="user_logout_notification_title" msgid="1453960926437240727">"အသုံးပြုသူ ထွက်လိုက်ပါ"</string>
<string name="user_logout_notification_text" msgid="3350262809611876284">"လက်ရှိ အသုံးပြုသူကို ထုတ်ပစ်ရန်"</string>
@@ -403,15 +410,15 @@
<string name="disable_vpn" msgid="4435534311510272506">"VPN ကို ပိတ်ထားရန်"</string>
<string name="disconnect_vpn" msgid="1324915059568548655">"VPN ကို အဆက်ဖြတ်ရန်"</string>
<string name="monitoring_description_device_owned" msgid="5780988291898461883">"သင့်စက်ကိရိယာကို<xliff:g id="ORGANIZATION">%1$s</xliff:g>\n\nမှစီမံခန့်ခွဲထားပါသည်။သင့်စီမံခန့်ခွဲသူသည် ကြိုတင်ပြင်ဆင်မှုများ၊စုပေါင်းဝင်ရောက်ခွင့်၊အက်ပလီကေးရှင်းများ၊သင့်စက်ကိရိယာနှင့်ဆက်နွယ်နေသောအချက်အလက်နှင့်သင့်စက်ကိရိယာ ရဲ့နေရာအချက်အလက်များကိုကြီးကြပ်စောင့်ကြည့်နိုင်ပါသည်။အသေးစိတ်သိရှိလိုပါကသင်၏စီမံခန့်ခွဲသူကိုဆက်သွယ်ပါ။"</string>
- <string name="monitoring_description_vpn" msgid="4445150119515393526">"VPN ချိတ်ဆက်မှုပြုလုပ်ရန် အပ်ဖ်ကို သင်ခွင့်ပြုလိုက်သည်။ \n\n ဤအပ်ဖ်သည် အီးမေးလ်များ၊ အပ်ဖ်များနှင့် ဝက်ဘ်ဆိုက်များအပါအဝင် သင့်ကိုယ်ရေးကိုယ်တာကွန်ရပ် လှုပ်ရှားမှုကို စောင့်ကြည့်နိုင်သည်။"</string>
+ <string name="monitoring_description_vpn" msgid="4445150119515393526">"VPN ချိတ်ဆက်မှုပြုလုပ်ရန် အက်ပ်ကို သင်ခွင့်ပြုလိုက်သည်။ \n\n ဤအက်ပ်သည် အီးမေးလ်များ၊ အက်ပ်များနှင့် ဝဘ်ဆိုက်များအပါအဝင် သင့်ကွန်ရက်လုပ်ဆောင်ချက်များကို စောင့်ကြည့်နိုင်သည်။"</string>
<string name="monitoring_description_vpn_device_owned" msgid="3090670777499161246">"သင့်စက်ကိရိယာကို<xliff:g id="ORGANIZATION">%1$s</xliff:g>\n \n မှစီမံခန့်ခွဲထားပါသည်။သင့်စီမံခန့်ခွဲသူသည် ကြိုတင်ပြင်ဆင်မှုများ၊စုပေါင်းဝင်ရောက်ခွင့်၊အက်ပလီကေးရှင်းများ၊သင့်စက်ကိရိယာနှင့်ဆက်နွယ်နေသောအချက်အလက်နှင့်သင့်စက်ကိရိယာ ရဲ့နေရာအချက်အလက်များကိုကြီးကြပ်စောင့်ကြည့်နိုင်ပါသည်။ \n\n အီးမေးလ်များ၊အက်ပလီကေးရှင်းများနှင့် ဝက်ဘ်ဆိုက်များအပါအဝင်သင့်ကွန်ယက်လုပ်ဆောင်ချက်ကိုစောင့်ကြည့်နိုင်သည့် VPN ကိုချိတ်ဆက်ထားပြီဖြစ် သည်။\n\n အသေးစိတ်သိရှိလိုပါကသင်၏စီမံခန့်ခွဲသူကိုဆက်သွယ်ပါ။"</string>
- <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"သင့်အလုပ်ပရိုဖိုင်ကို <xliff:g id="ORGANIZATION">%1$s</xliff:g>မှ စီမံခန့်ခွဲပါသည်။ \n\nသင့်စီမံခန့်ခွဲသူသည် အီးမေးလ်များ၊ အပ်ဖ်များ၊ နှင့် ဝက်ဘ်ဆိုက်များအပါအဝင် သင့်ကွန်ရက်လှုပ်ရှားမှုကို စောင့်ကြည့်နိုင်သည်။ \n\nပိုမိုသော သတင်းအချက်အလက်အတွက်၊ သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။ \n\nသင်သည် VPN သို့လည်းဆက်သွယ်ထားပြီး၊ ၎င်းသည် သင့်ကွန်ရက်လှုပ်ရှားမှုကို စောင့်ကြည့်နိုင်ပါသည်။"</string>
+ <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"သင့်အလုပ်ပရိုဖိုင်ကို <xliff:g id="ORGANIZATION">%1$s</xliff:g>မှ စီမံခန့်ခွဲပါသည်။ \n\nသင့်စီမံခန့်ခွဲသူသည် အီးမေးလ်များ၊ အက်ပ်များ၊ နှင့် ဝဘ်ဆိုက်များအပါအဝင် သင့်ကွန်ရက်လုပ်ဆောက်ချက်ကို စောင့်ကြည့်နိုင်သည်။ \n\nအချက်အလက်များ ပိုမိုရယူရန် သင်၏ စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။ \n\nသင်သည် VPN တစ်ခုသို့ပါ ချိတ်ဆက်ထားပြီး ၎င်းကပါ သင်၏ ကွန်ရက်လုပ်ဆောင်ချက်ကို စောင့်ကြည့်နိုင်ပါသည်။"</string>
<string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
- <string name="monitoring_description_app" msgid="6259179342284742878">"သင်သည် <xliff:g id="APPLICATION">%1$s</xliff:g> သို့ချိတ်ဆက်ထားသည်၊ ၎င်းသည် အီးမေးများ၊ အပ်ဖ်များနှင့် ဝက်ဘ်ဆိုက်များအပါအဝင် သင့်ကွန်ရက်လှုပ်ရှားမှုများကို စောင့်ကြည့်နိုင်သည်။"</string>
- <string name="monitoring_description_app_personal" msgid="484599052118316268">"သင်သည် <xliff:g id="APPLICATION">%1$s</xliff:g> သို့ချိတ်ဆက်ထားသည်၊ ၎င်းသည် အီးမေးလ်များ၊ အပ်ဖ်များနှင့် ဝက်ဘ်ဆိုက်များအပါအဝင် သင့်ကိုယ်ရေးကိုယ်တာကွန်ရပ် လှုပ်ရှားမှုကို စောင့်ကြည့်နိုင်သည်။"</string>
- <string name="monitoring_description_app_work" msgid="1754325860918060897">"သင့်အလုပ် ပရိုဖိုင်ကို <xliff:g id="ORGANIZATION">%1$s</xliff:g> မှစီမံခန့်ခွဲသည်။ ၎င်းကို <xliff:g id="APPLICATION">%2$s</xliff:g> သို့ချိတ်ဆက်ထားသည်၊ ၎င်းသည် အီးမေးလ်များ၊ အပ်ဖ်များနှင့် ဝက်ဘ်ဆိုက်များအပါအဝင် သင့်ကိုယ်ရေးကိုယ်တာကွန်ရပ် လှုပ်ရှားမှုကို စောင့်ကြည့်နိုင်သည်။ \n\nနောက်ထပ်အချက်အလက်များအတွက်၊ သင့်ကြီးကြပ်သူကို ဆက်သွယ်ပါ။"</string>
- <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"သင့်အလုပ် ပရိုဖိုင်ကို <xliff:g id="ORGANIZATION">%1$s</xliff:g> မှစီမံခန့်ခွဲသည်။ ၎င်းကို <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> သို့ချိတ်ဆက်ထားသည်၊ ၎င်းသည် အီးမေးလ်များ၊ အပ်ဖ်များနှင့် ဝက်ဘ်ဆိုက်များအပါအဝင် သင့်ကိုယ်ရေးကိုယ်တာကွန်ရပ် လှုပ်ရှားမှုကို စောင့်ကြည့်နိုင်သည်။ \n\n သင်သည်<xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> ကိုလည်းချိတ်ဆက်ထားသည်၊ ၎င်းသည် သင့်ကိုယ်ရေးကိုယ်တာ ကွန်ရက် လှုပ်ရှားမှုများကို စောင့်ကြည့်နိုင်သည်။"</string>
- <string name="monitoring_description_vpn_app_device_owned" msgid="4970443827043261703">"သင့်စက်ကိရိယာကို <xliff:g id="ORGANIZATION">%1$s</xliff:g> မှစီမံခန့်ခွဲပါသည်။ \n\n သင်စီမံခန့်ခွဲသူသည် ကြိုတင်ပြင်ဆင်မှုများ၊ စုပေါင်းဝင်ရောက်ခွင့်၊ အပ်ဖ်များ၊ သင့်ကိရိယာနှင့် သက်ဆိုင်သော ဒေတာ၊ နှင့် သင့်ကိရိယာ၏ တည်နေရာအချိက်အလက်များကို စောင့်ကြည့်ပြီး စီမံခန့်ခွဲနိုင်သည်။ \n\nသင်သည် <xliff:g id="APPLICATION">%2$s</xliff:g> သို့ချိတ်ဆက်ထားသည်၊ ၎င်းသည် အီးမေးလ်းများ၊ အပ်ဖ်များ၊ နှင့် ဝက်ဘ်ဆိုက်များအပါအဝင် သင့်ကွန်ရက်လှုပ်ရှားမှုကို စောင့်ကြည့်နိုင်သည်။ \n\nပိုမိုသော သတင်းအချက်အလက်အတွက်၊ သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။"</string>
+ <string name="monitoring_description_app" msgid="6259179342284742878">"သင်သည် <xliff:g id="APPLICATION">%1$s</xliff:g> သို့ ချိတ်ဆက်ထားသည်၊ ၎င်းသည် အီးမေးများ၊ အက်ပ်များနှင့် ဝဘ်ဆိုက်များအပါအဝင် သင့်ကွန်ရက်လုပ်ဆောင်ချက်များကို စောင့်ကြည့်နိုင်သည်။"</string>
+ <string name="monitoring_description_app_personal" msgid="484599052118316268">"သင်သည် <xliff:g id="APPLICATION">%1$s</xliff:g> သို့ ချိတ်ဆက်ထားသည်။ ၎င်းသည် အီးမေးလ်များ၊ အက်ပ်များနှင့် ဝဘ်ဆိုက်များအပါအဝင် သင့်ကွန်ရက်လုပ်ဆောင်ချက်ကို စောင့်ကြည့်နိုင်သည်။"</string>
+ <string name="monitoring_description_app_work" msgid="1754325860918060897">"သင့်အလုပ်ပရိုဖိုင်ကို <xliff:g id="ORGANIZATION">%1$s</xliff:g> မှစီမံခန့်ခွဲသည်။ ၎င်းကို <xliff:g id="APPLICATION">%2$s</xliff:g> သို့ ချိတ်ဆက်ထားသည်၊ ၎င်းသည် အီးမေးလ်များ၊ အက်ပ်များနှင့် ဝဘ်ဆိုက်များအပါအဝင် သင့်အလုပ်ကွန်ရက်လုပ်ဆောင်ချက်ကို စောင့်ကြည့်နိုင်သည်။ \n\nအချက်အလက်များ ပိုမိုရယူရန် သင်၏ စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။"</string>
+ <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"သင့်အလုပ် ပရိုဖိုင်ကို <xliff:g id="ORGANIZATION">%1$s</xliff:g> မှစီမံခန့်ခွဲသည်။ ၎င်းကို <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> သို့ ချိတ်ဆက်ထားသည်၊ ၎င်းသည် အီးမေးလ်များ၊ အက်ပ်များနှင့် ဝဘ်ဆိုက်များအပါအဝင် သင့်အလုပ်ကွန်ရက်လုပ်ဆောင်ချက်ကို စောင့်ကြည့်နိုင်သည်။ \n\n သင်သည်<xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> ကိုလည်း ချိတ်ဆက်ထားသည်၊ ၎င်းသည် သင့်ကိုယ်ပိုင်ကွန်ရက်လုပ်ဆောင်ချက်များကို စောင့်ကြည့်နိုင်သည်။"</string>
+ <string name="monitoring_description_vpn_app_device_owned" msgid="4970443827043261703">"သင့်စက်ကိရိယာကို <xliff:g id="ORGANIZATION">%1$s</xliff:g> မှစီမံခန့်ခွဲပါသည်။ \n\n စီမံခန့်ခွဲသူသည် ကြိုတင်ပြင်ဆင်မှုများ၊ စုပေါင်းဝင်ရောက်ခွင့်၊ အက်ပ်များ၊ သင့်ကိရိယာနှင့်သက်ဆိုင်သော ဒေတာနှင့် သင့်ကိရိယာ၏ တည်နေရာအချက်အလက်များကို စောင့်ကြည့်ပြီး စီမံခန့်ခွဲနိုင်သည်။ \n\nသင်သည် <xliff:g id="APPLICATION">%2$s</xliff:g> သို့ချိတ်ဆက်ထားသည်၊ ၎င်းသည် အီးမေးလ်များ၊ အက်ပ်များ၊ နှင့် ဝဘ်ဆိုက်များအပါအဝင် သင့်ကွန်ရက်လုပ်ဆောင်ချက်ကို စောင့်ကြည့်နိုင်သည်။ \n\nအချက်အလက်များ ပိုမိုရယူရန် သင်၏ စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။"</string>
<string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"သင်က လက်ဖြင့် သော့မဖွင့်မချင်း ကိရိယာမှာ သော့ပိတ်လျက် ရှိနေမည်"</string>
<string name="hidden_notifications_title" msgid="7139628534207443290">"အကြောင်းကြားချက်များ မြန်မြန်ရရန်"</string>
<string name="hidden_notifications_text" msgid="2326409389088668981">"မဖွင့်ခင် ၎င်းတို့ကို ကြည့်ပါ"</string>
@@ -434,10 +441,22 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် အသံဒိုင်ယာလော့ခ်ဖြစ်သည်"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"မူရင်းကိုပြန်ယူရန် တို့ပါ။"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"သင်သည် အလုပ်ပရိုဖိုင်းအား သုံးနေသည်"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"ခေါ်ဆိုမှု"</item>
+ <item msgid="5997713001067658559">"စနစ်"</item>
+ <item msgid="7858983209929864160">"ဖုန်းခေါ်ဆိုမှု"</item>
+ <item msgid="1850038478268896762">"မီဒီယာ"</item>
+ <item msgid="8265110906352372092">"နှိုးစက်"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"ဘလူးတုသ်"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s။ အသံပြန်ဖွင့်ရန် တို့ပါ။"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s။ တုန်ခါမှုကို သတ်မှတ်ရန် တို့ပါ။ အများသုံးစွဲနိုင်မှု ဝန်ဆောင်မှုများကို အသံပိတ်ထားနိုင်ပါသည်။"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s။ အသံပိတ်ရန် တို့ပါ။ အများသုံးစွဲနိုင်မှု ဝန်ဆောင်မှုများကို အသံပိတ်ထားနိုင်ပါသည်။"</string>
- <string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"အသံအတိုးအလျှော့ခလုတ် %s ပြသထားပါသည်။ ပယ်ဖျက်ရန် ပွတ်ဆွဲပါ။"</string>
+ <string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"အသံအတိုးအလျှော့ခလုတ် %s ပြသထားပါသည်။ ပယ်ရန် အပေါ်သို့ပွတ်ဆွဲပါ။"</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"အသံအတိုးအလျှော့ခလုတ်များကို ဝှက်ထားပါသည်"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"စနစ် UI ဖမ်းစက်"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"မြုတ်ထားသည့် ဘက်ထရီ ရာခိုင်နှုန်းကို ပြပါ"</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"နောက်ထပ် ဆက်တင်များ"</string>
<string name="notification_done" msgid="5279426047273930175">"ပြီးပါပြီ"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> အကြောင်းကြားချက် ထိန်းချုပ်မှုများ"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"အရောင်နှင့် အပြင်အဆင်"</string>
- <string name="night_mode" msgid="3540405868248625488">"ညသုံးမုဒ်"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"ပြသမှုအချိန်အဆကို ညှိပါ"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"ဖွင့်ပါ"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"ပိတ်ပါ"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"အလိုအလျောက် ဖွင့်ပါ"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"တည်နေရာနှင့် တစ်ရက်တာအချိန်နှင့် သင့်လျော်သလို ညသုံးမုဒ်သို့ ပြောင်းပါ"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"ညသုံမုဒ်ဖွင့်ထားစဉ်"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android OS အတွက်အရောင်ရင့်အပြင်အဆင်ကို သုံးပါ"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"အရောင်မွဲမှုကို ချိန်ပါ"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"အလင်းအမှောင်ချိန်ပါ"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"အရောင်ရင့်အပြင်အဆင်သည် ဆက်တင်များကဲ့သို့ ပုံမှန်အားဖြင့် အရောင်ဖျော့အပြင်အဆင်အဖြစ်ရှိသည့် Android OS ၏အဓိကနေရာများကို ပြောင်းလဲပေးပါသည်။"</string>
- <string name="color_apply" msgid="9212602012641034283">"အသုံးပြုပါ"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"ဆက်တင်များကို အတည်ပြုပါ"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"အချို့သော အရောင်ဆက်တက်များက ဤကိရိယာကို သုံးမရအောင် လုပ်ပစ်နိုင်ပါသည်။ ဤအရောင် ဆက်တင်များကို အတည်ပြုရန် အိုကေကို နှိပ်ပါ၊ သို့မဟုတ် ဤဆက်တင်များကို ၁၀ စက္ကန့် အကြာတွင် ပြန်ညှိလိုက်ပါမည်။"</string>
<string name="battery_panel_title" msgid="7944156115535366613">"ဘက်ထရီ အသုံးပြုမှု"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"အားသွင်းနေချိန်မှာ Battery Saver ကို သုံးမရပါ"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Battery Saver"</string>
@@ -543,7 +547,7 @@
<string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"ရှေ့သို့ရစ်ပါ"</string>
<string name="keyboard_key_page_up" msgid="5654098530106845603">"အပေါ်စာမျက်နှာသို့သွားပါ"</string>
<string name="keyboard_key_page_down" msgid="8720502083731906136">"အောက်စာမျက်နှာသို့သွားပါ"</string>
- <string name="keyboard_key_forward_del" msgid="1391451334716490176">"ဖျက်ပါ"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"ဖျက်ရန်"</string>
<string name="keyboard_key_move_home" msgid="2765693292069487486">"ပင်မ"</string>
<string name="keyboard_key_move_end" msgid="5901174332047975247">"ပြီးပါပြီ"</string>
<string name="keyboard_key_insert" msgid="8530501581636082614">"ထည့်ပါ"</string>
@@ -587,7 +591,7 @@
<string name="menu_ime" msgid="4943221416525250684">"မန်နယူး / ကီးဘုတ်ပြောင်းစနစ်"</string>
<string name="select_button" msgid="1597989540662710653">"ပေါင်းထည့်ရန် ခလုတ်ကိုရွေးပါ"</string>
<string name="add_button" msgid="4134946063432258161">"ခလုတ်ပေါင်းထည့်ပါ"</string>
- <string name="save" msgid="2311877285724540644">"သိမ်းဆည်းပါ"</string>
+ <string name="save" msgid="2311877285724540644">"သိမ်းရန်"</string>
<string name="reset" msgid="2448168080964209908">"ပြန်လည်သတ်မှတ်ရန်"</string>
<string name="no_home_title" msgid="1563808595146071549">"ပင်မခလုတ်မတွေ့ပါ"</string>
<string name="no_home_message" msgid="5408485011659260911">"ဤစက်ပစ္စည်းကိုရွှေ့လျားနိုင်ရန် ပင်မခလုတ် လိုအပ်ပါသည်။ မသိမ်းဆည်းမီ ပင်မခလုတ်ကို ပေါင်းထည့်ပါ။"</string>
diff --git a/packages/SystemUI/res/values-nb/config.xml b/packages/SystemUI/res/values-nb/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-nb/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index fc81c3c..3efdd3e 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3,5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Grense på <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Advarsel for <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Arbeidsmodus"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Nattlys"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Nattlys er på, trykk for å slå av"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Nattlys er av, trykk for å slå på"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Ingen nylige elementer"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Du har fjernet alt"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Appinformasjon"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Del horisontalt"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Del vertikalt"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Del tilpasset"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Oppladet"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Lader"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Fulladet om <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> er volumdialogen"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Trykk for å gjenopprette originalen."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Du bruker jobbprofilen din"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Ring"</item>
+ <item msgid="5997713001067658559">"System"</item>
+ <item msgid="7858983209929864160">"Varsellyd"</item>
+ <item msgid="1850038478268896762">"Medier"</item>
+ <item msgid="8265110906352372092">"Alarmen"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Trykk for å slå på lyden."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Trykk for å angi vibrasjon. Lyden kan bli slått av for tilgjengelighetstjenestene."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Trykk for å slå av lyden. Lyden kan bli slått av for tilgjengelighetstjenestene."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Flere innstillinger"</string>
<string name="notification_done" msgid="5279426047273930175">"Ferdig"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Varselinnstillinger for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Farge og utseende"</string>
- <string name="night_mode" msgid="3540405868248625488">"Nattmodus"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Kalibrer skjermen"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"På"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Av"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Slå på automatisk"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Bytt til nattmodus avhengig av tid og sted"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Når nattmodus er på"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Bruk et mørkt tema for Android OS"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Juster fargen"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Juster lysstyrken"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Det mørke temaet brukes på kjerneområdene i Android OS som vanligvis vises i et lyst tema, for eksempel Innstillinger."</string>
- <string name="color_apply" msgid="9212602012641034283">"Bruk"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Bekreft innstillingene"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Noen fargeinnstillinger kan gjøre denne enheten ubrukelig. Klikk på OK for å bekrefte disse fargeinnstillingene, ellers blir de tilbakestilt etter ti sekunder."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Batteribruk"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Batterisparing er ikke tilgjengelig under lading"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Batterisparing"</string>
diff --git a/packages/SystemUI/res/values-ne-rNP/config.xml b/packages/SystemUI/res/values-ne-rNP/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-ne-rNP/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml
index 4f321cf..07d0795 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"रोमिङ"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> सीमा"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> चेतावनी दिँदै"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"कार्य मोड"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"रात्रिको प्रकाश"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"रात्रिको प्रकाश सक्रिय छ, निष्क्रिय पार्न ट्याप गर्नुहोस्"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"रात्रिको प्रकाश निष्क्रिय छ, सक्रिय गर्न ट्याप गर्नुहोस्"</string>
<string name="recents_empty_message" msgid="808480104164008572">"हालका कुनै पनि वस्तुहरू छैनन्"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"तपाईँले सबै कुरा खाली गर्नुभएको छ"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"अनुप्रयोग जानकारी"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"तेर्सो रूपमा विभाजन गर्नुहोस्"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ठाडो रूपमा विभाजन गर्नुहोस्"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"अनुकूलन विभाजन गर्नुहोस्"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"चार्ज भयो"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"चार्ज हुँदै"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> पूर्ण नभएसम्म"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> भोल्यूम संवाद हो"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"मूललाई पुनर्स्थापना गर्न ट्याप गर्नुहोस्।"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"तपाईँले कार्य प्रोफाइल प्रयोग गर्दै हुनुहुन्छ"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"कल"</item>
+ <item msgid="5997713001067658559">"प्रणाली"</item>
+ <item msgid="7858983209929864160">"रिङटोन"</item>
+ <item msgid="1850038478268896762">"मिडिया"</item>
+ <item msgid="8265110906352372092">"अलार्म"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"ब्लुटुथ"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s। अनम्यूट गर्नका लागि ट्याप गर्नुहोस्।"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s। कम्पनमा सेट गर्नका लागि ट्याप गर्नुहोस्। पहुँच सम्बन्धी सेवाहरू म्यूट हुन सक्छन्।"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s। म्यूट गर्नका लागि ट्याप गर्नुहोस्। पहुँच सम्बन्धी सेवाहरू म्यूट हुन सक्छन्।"</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"थप सेटिङहरू"</string>
<string name="notification_done" msgid="5279426047273930175">"सम्पन्न भयो"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> का सूचनाका लागि नियन्त्रणहरू"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"रंग र रूप"</string>
- <string name="night_mode" msgid="3540405868248625488">"रात्री मोड"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"प्रदर्शनको स्तर मिलाउनुहोस्"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"सक्रिय"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"निष्क्रिय"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"स्वतः सक्रिय पार्नुहोस्"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"स्थान र दिनको समयको लागि उपयुक्त रात्री मोडमा स्विच गर्नुहोस्"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"रात्री मोड सक्रिय हुँदा"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android OS का लागि गाढा रंगको विषयवस्तु प्रयोग गर्नुहोस्"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"रङ्गलाई समायोजन गर्नुहोस्"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"चमकलाई समायोजन गर्नुहोस्"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"गहिरो रंगको विषयवस्तुलाई Android OS का त्यस्ता मुख्य क्षेत्रहरूमा लागू गरिन्छ जसलाई सामान्यतया हल्का रंगमा देखाइन्छ, जस्तै सेटिङहरू।"</string>
- <string name="color_apply" msgid="9212602012641034283">"लागू गर्नुहोस्"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"सेटिङहरूको पुष्टि गर्नुहोस्"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"केही रङ सेटिङहरूले यस यन्त्रलाई अनुपयोगी बनाउन सक्छन्। यी रङ सेटिङहरू पुष्टि गर्न ठीक छ मा क्लिक गर्नुहोस्, अन्यथा यी सेटिङहरू १० सेकेण्डपछि रिसेट हुनेछन्।"</string>
<string name="battery_panel_title" msgid="7944156115535366613">"ब्याट्री उपयोग"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"चार्ज गर्ने समयमा ब्याट्री सेभर उपलब्ध छैन"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"ब्याट्री सेभर"</string>
diff --git a/packages/SystemUI/res/values-nl/config.xml b/packages/SystemUI/res/values-nl/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-nl/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 72f04bb..9509229 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Limiet van <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Waarschuwing voor <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Werkmodus"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Nachtverlichting"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Nachtverlichting is ingeschakeld. Tik om deze uit te schakelen."</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Nachtverlichting is uitgeschakeld. Tik om deze in te schakelen."</string>
<string name="recents_empty_message" msgid="808480104164008572">"Geen recente items"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Je hebt alles gewist"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"App-informatie"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Horizontaal splitsen"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Verticaal splitsen"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Aangepast splitsen"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Opgeladen"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Opladen"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> tot volledig opgeladen"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> is het volumedialoogvenster"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Tik om het origineel te herstellen."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"U gebruikt je werkprofiel"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Bellen"</item>
+ <item msgid="5997713001067658559">"Systeem"</item>
+ <item msgid="7858983209929864160">"Bellen"</item>
+ <item msgid="1850038478268896762">"Media"</item>
+ <item msgid="8265110906352372092">"Alarm"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Tik om dempen op te heffen."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tik om in te stellen op trillen. Toegankelijkheidsservices kunnen zijn gedempt."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Tik om te dempen. Toegankelijkheidsservices kunnen zijn gedempt."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Meer instellingen"</string>
<string name="notification_done" msgid="5279426047273930175">"Gereed"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Beheeropties voor <xliff:g id="APP_NAME">%1$s</xliff:g>-meldingen"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Kleur en uiterlijk"</string>
- <string name="night_mode" msgid="3540405868248625488">"Nachtmodus"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Display kalibreren"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Aan"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Uit"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Automatisch inschakelen"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Overschakelen naar nachtmodus indien van toepassing voor locatie en tijd van de dag"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Als nachtmodus is ingeschakeld"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Donker thema gebruiken voor Android OS"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Tint aanpassen"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Helderheid aanpassen"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Het donkere thema wordt toegepast op kerngedeelten van het Android-besturingssysteem die normaal gesproken worden weergegeven met een licht thema, zoals Instellingen."</string>
- <string name="color_apply" msgid="9212602012641034283">"Toepassen"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Instellingen bevestigen"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Bij sommige kleurinstellingen kan het apparaat onbruikbaar worden. Klik op OK om deze kleurinstellingen te bevestigen, anders worden deze instellingen na tien seconden gereset."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Accugebruik"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Accubesparing niet beschikbaar tijdens opladen"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Accubesparing"</string>
diff --git a/packages/SystemUI/res/values-pa-rIN/config.xml b/packages/SystemUI/res/values-pa-rIN/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-pa-rIN/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-pa-rIN/strings.xml b/packages/SystemUI/res/values-pa-rIN/strings.xml
index d9cb04f..17c1261 100644
--- a/packages/SystemUI/res/values-pa-rIN/strings.xml
+++ b/packages/SystemUI/res/values-pa-rIN/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"ਰੋਮਿੰਗ"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"ਕਿਨਾਰਾ"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ਸੀਮਾ"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ਚਿਤਾਵਨੀ"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"ਕੰਮ ਮੋਡ"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"ਰਾਤਰੀ ਲਾਈਟ"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"ਰਾਤਰੀ ਲਾਈਟ ਚਾਲੂ ਹੈ, ਬੰਦ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"ਰਾਤਰੀ ਲਾਈਟ ਬੰਦ ਹੈ, ਚਾਲੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="recents_empty_message" msgid="808480104164008572">"ਕੋਈ ਹਾਲੀਆ ਆਈਟਮਾਂ ਨਹੀਂ"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"ਤੁਸੀਂ ਸਭ ਕੁਝ ਸਾਫ਼ ਕਰ ਦਿੱਤਾ ਹੈ"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"ਐਪਲੀਕੇਸ਼ਨ ਜਾਣਕਾਰੀ"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ਹੌਰੀਜ਼ੌਂਟਲ ਸਪਲਿਟ"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ਵਰਟੀਕਲ ਸਪਲਿਟ"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ਕਸਟਮ ਸਪਲਿਟ"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"ਚਾਰਜ ਹੋਇਆ"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"ਚਾਰਜ ਕਰ ਰਿਹਾ ਹੈ"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> ਪੂਰਾ ਹੋਣ ਤੱਕ"</string>
@@ -344,8 +351,7 @@
<string name="zen_silence_introduction" msgid="3137882381093271568">"ਇਹ ਅਲਾਰਮ, ਸੰਗੀਤ, ਵੀਡੀਓਜ਼, ਅਤੇ ਗੇਮਸ ਸਮੇਤ, ਸਾਰੀਆਂ ਧੁਨੀਆਂ ਅਤੇ ਵਾਇਬ੍ਰੇਸ਼ਨ ਨੂੰ ਬਲੌਕ ਕਰਦਾ ਹੈ।"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"ਹੇਠਾਂ ਘੱਟ ਲਾਜ਼ਮੀ ਸੂਚਨਾਵਾਂ"</string>
- <!-- no translation found for notification_tap_again (7590196980943943842) -->
- <skip />
+ <string name="notification_tap_again" msgid="7590196980943943842">"ਖੋਲ੍ਹਣ ਲਈ ਦੁਬਾਰਾ ਟੈਪ ਕਰੋ"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"ਅਨਲੌਕ ਕਰਨ ਲਈ ਉੱਪਰ ਸਵਾਈਪ ਕਰੋ।"</string>
<string name="phone_hint" msgid="4872890986869209950">"ਫ਼ੋਨ ਲਈ ਆਈਕਨ ਤੋਂ ਸਵਾਈਪ ਕਰੋ"</string>
<string name="voice_hint" msgid="8939888732119726665">"ਵੌਇਸ ਅਸਿਸਟ ਲਈ ਆਈਕਨ ਤੋਂ ਸਵਾਈਪ ਕਰੋ"</string>
@@ -435,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਵੋਲਯੂਮ ਡਾਇਲੌਗ ਹੈ"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"ਅਸਲ ਨੂੰ ਮੁੜ-ਬਹਾਲ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"ਤੁਸੀਂ ਆਪਣੀ ਕੰਮ ਪ੍ਰੋਫਾਈਲ ਵਰਤ ਰਹੇ ਹੋ"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"ਕਾਲ ਕਰੋ"</item>
+ <item msgid="5997713001067658559">"ਸਿਸਟਮ"</item>
+ <item msgid="7858983209929864160">"ਰਿੰਗ ਕਰੋ"</item>
+ <item msgid="1850038478268896762">"ਮੀਡੀਆ"</item>
+ <item msgid="8265110906352372092">"ਅਲਾਰਮ"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"ਬਲੂਟੁੱਥ"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s। ਅਣਮਿਊਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s। ਥਰਥਰਾਹਟ ਸੈੱਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ। ਪਹੁੰਚਯੋਗਤਾ ਸੇਵਾਵਾਂ ਮਿਊਟ ਹੋ ਸਕਦੀਆਂ ਹਨ।"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s। ਮਿਊਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ। ਪਹੁੰਚਯੋਗਤਾ ਸੇਵਾਵਾਂ ਮਿਊਟ ਹੋ ਸਕਦੀਆਂ ਹਨ।"</string>
@@ -505,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"ਹੋਰ ਸੈਟਿੰਗਾਂ"</string>
<string name="notification_done" msgid="5279426047273930175">"ਹੋ ਗਿਆ"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਸੂਚਨਾ ਕੰਟਰੋਲ"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"ਰੰਗ ਅਤੇ ਵਿਖਾਲਾ"</string>
- <string name="night_mode" msgid="3540405868248625488">"ਰਾਤ ਮੋਡ"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"ਡਿਸਪਲੇ ਨੂੰ ਕੈਲੀਬ੍ਰੇਟ ਕਰੋ"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"ਚਾਲੂ"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"ਬੰਦ"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"ਸਵੈਚਾਲਿਤ ਤੌਰ \'ਤੇ ਚਾਲੂ ਕਰੋ"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"ਟਿਕਾਣੇ ਅਤੇ ਦਿਨ ਦੇ ਸਮੇਂ ਲਈ ਢੁਕਵੇਂ ਰਾਤ ਮੋਡ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"ਜਦੋਂ ਰਾਤ ਮੋਡ ਚਾਲੂ ਹੋਵੇ"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android OS ਲਈ ਗੂੜ੍ਹੇ ਥੀਮ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"ਟਿੰਟ ਨੂੰ ਵਿਵਸਥਿਤ ਕਰੋ"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"ਚਮਕ ਨੂੰ ਵਿਵਸਥਿਤ ਕਰੋ"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"ਗੂੜ੍ਹੇ ਥੀਮ ਨੂੰ Android OS ਦੇ ਉਹਨਾਂ ਮੁੱਖ ਖੇਤਰਾਂ \'ਤੇ ਲਾਗੂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ ਜੋ ਆਮ ਤੌਰ \'ਤੇ ਇੱਕ ਹਲਕੇ ਥੀਮ ਵਿੱਚ ਵਿਖਾਏ ਜਾਂਦੇ ਹਨ, ਜਿਵੇਂ ਕਿ ਸੈਟਿੰਗਾਂ।"</string>
- <string name="color_apply" msgid="9212602012641034283">"ਲਾਗੂ ਕਰੋ"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"ਸੈਟਿੰਗਾਂ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"ਕੁਝ ਰੰਗ ਸੈਟਿੰਗਾਂ ਇਸ ਡੀਵਾਈਸ ਨੂੰ ਬੇਕਾਰ ਕਰ ਸਕਦੀਆਂ ਹਨ। ਇਹਨਾਂ ਰੰਗ ਸੈਟਿੰਗਾਂ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨ ਲਈ ਠੀਕ \'ਤੇ ਕਲਿੱਕ ਕਰੋ, ਨਹੀਂ ਤਾਂ ਇਹ ਸੈਟਿੰਗਾਂ 10 ਸਕਿੰਟ ਬਾਅਦ ਮੁੜ-ਸੈੱਟ ਹੋ ਜਾਣਗੀਆਂ।"</string>
<string name="battery_panel_title" msgid="7944156115535366613">"ਬੈਟਰੀ ਵਰਤੋਂ"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ਬੈਟਰੀ ਸੇਵਰ ਚਾਰਜਿੰਗ ਦੌਰਾਨ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"ਬੈਟਰੀ ਸੇਵਰ"</string>
diff --git a/packages/SystemUI/res/values-pl/config.xml b/packages/SystemUI/res/values-pl/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-pl/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 3cf8294..c86d26c 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -145,7 +145,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -321,6 +323,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Limit <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Ostrzeżenie: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Tryb pracy"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Podświetlenie nocne"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Podświetlenie nocne włączone – kliknij, by wyłączyć"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Podświetlenie nocne wyłączone – kliknij, by włączyć"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Brak ostatnich elementów"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Wszystko zostało wyczyszczone"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacje o aplikacji"</string>
@@ -334,6 +339,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podziel poziomo"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Podziel pionowo"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Podziel niestandardowo"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Naładowana"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Ładowanie"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> do pełnego naładowania"</string>
@@ -438,6 +445,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> steruje głośnością"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Kliknij, by przywrócić ustawienie początkowe."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Używasz profilu do pracy"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Połączenie"</item>
+ <item msgid="5997713001067658559">"System"</item>
+ <item msgid="7858983209929864160">"Dzwonek"</item>
+ <item msgid="1850038478268896762">"Multimedia"</item>
+ <item msgid="8265110906352372092">"Alarm"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Kliknij, by wyłączyć wyciszenie."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Kliknij, by włączyć wibracje. Ułatwienia dostępu mogą być wyciszone."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Kliknij, by wyciszyć. Ułatwienia dostępu mogą być wyciszone."</string>
@@ -508,21 +527,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Więcej ustawień"</string>
<string name="notification_done" msgid="5279426047273930175">"Gotowe"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> – ustawienia powiadomień"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Kolor i wygląd"</string>
- <string name="night_mode" msgid="3540405868248625488">"Tryb nocny"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Kalibracja wyświetlacza"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Wł."</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Wył."</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Włącz automatycznie"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Przełączaj na tryb nocny odpowiednio do lokalizacji i pory dnia"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Gdy jest włączony tryb nocny"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Użyj motywu ciemnego dla Androida"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Dostosuj odcień"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Dostosuj jasność"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Motyw ciemny zostanie zastosowany do głównych obszarów Androida, które normalnie są jasne, takich jak Ustawienia."</string>
- <string name="color_apply" msgid="9212602012641034283">"Zastosuj"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Potwierdź ustawienia"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Niektóre ustawienia kolorów mogą utrudniać korzystanie z urządzenia. Kliknij OK, by potwierdzić te ustawienia kolorów. Jeśli tego nie zrobisz, zostaną one zresetowane po 10 sekundach."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Wykorzystanie baterii"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Oszczędzanie baterii nie jest dostępne podczas ładowania"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Oszczędzanie baterii"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/config.xml b/packages/SystemUI/res/values-pt-rBR/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-pt-rBR/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 6feea20..199dbad 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -319,6 +321,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Limite: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Aviso de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Modo de trabalho"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Modo noturno"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Modo noturno ativado. Toque para desativar"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Modo noturno desativado. Toque para ativar"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Nenhum item recente"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Você limpou tudo"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informações do app"</string>
@@ -332,6 +337,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisão horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisão vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisão personalizada"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carregada"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Carregando"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> até concluir"</string>
@@ -436,6 +443,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> é a caixa de diálogo referente ao volume"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Toque para restaurar o original."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Você está usando seu perfil de trabalho"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Chamar"</item>
+ <item msgid="5997713001067658559">"Sistema"</item>
+ <item msgid="7858983209929864160">"Tocar"</item>
+ <item msgid="1850038478268896762">"Mídia"</item>
+ <item msgid="8265110906352372092">"Alarme"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Toque para ativar o som."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Toque para configurar para vibrar. É possível que os serviços de acessibilidade sejam silenciados."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Toque para silenciar. É possível que os serviços de acessibilidade sejam silenciados."</string>
@@ -506,21 +525,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Mais configurações"</string>
<string name="notification_done" msgid="5279426047273930175">"Concluído"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Controles de notificação do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Cor e aparência"</string>
- <string name="night_mode" msgid="3540405868248625488">"Modo noturno"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Calibrar tela"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Ativado"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Desativado"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Ativar automaticamente"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Alternar para o modo noturno conforme apropriado para o local e hora do dia"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Quando o modo noturno está ativado"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Usar o tema escuro para o SO Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Ajustar tonalidade"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Ajustar brilho"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"O tema escuro é aplicado a áreas centrais do sistema operacional Android que normalmente são exibidas em um tema claro, como as configurações."</string>
- <string name="color_apply" msgid="9212602012641034283">"Aplicar"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Confirmar configurações"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Algumas configurações de cor podem tornar o dispositivo inutilizável. Clique em \"OK\" para confirmar essas configurações de cor; caso contrário, essas configurações serão redefinidas após 10 segundos."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Uso da bateria"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"A Economia de bateria não fica disponível durante o carregamento"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Economia de bateria"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/config.xml b/packages/SystemUI/res/values-pt-rPT/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-pt-rPT/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index c393400..d3d7f24 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3,5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Limite de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Aviso de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Modo de trabalho"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Luz noturna"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Luz noturna ativada; toque para desativar"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Luz noturna desativada; toque para ativar"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Nenhum item recente"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Limpou tudo"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informações da aplicação"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisão horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisão vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisão personalizada"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carregada"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"A carregar"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> até ficar completa"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> é a caixa de diálogo do volume"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Toque para restaurar o original."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Está a utilizar o seu perfil de trabalho"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Telefonar"</item>
+ <item msgid="5997713001067658559">"Sistema"</item>
+ <item msgid="7858983209929864160">"Tocar"</item>
+ <item msgid="1850038478268896762">"Multimédia"</item>
+ <item msgid="8265110906352372092">"Alarme"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Toque para reativar o som."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Toque para ativar a vibração. Os serviços de acessibilidade podem ser silenciados."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Toque para desativar o som. Os serviços de acessibilidade podem ser silenciados."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Mais definições"</string>
<string name="notification_done" msgid="5279426047273930175">"Concluído"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Controlos de notificações do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Cor e aspeto"</string>
- <string name="night_mode" msgid="3540405868248625488">"Modo noturno"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Calibrar ecrã"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Ativado"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Desativado"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Ligar automaticamente"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Alternar para o Modo noturno consoante a localização e a hora do dia"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Quando o Modo noturno está ativado"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Utilizar o tema escuro para o SO Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Ajustar tonalidade"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Ajustar brilho"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"O tema escuro é aplicado a áreas essenciais do SO Android que são normalmente apresentadas num tema claro, como as Definições."</string>
- <string name="color_apply" msgid="9212602012641034283">"Aplicar"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Confirmar as definições"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Algumas definições de cor podem tornar este dispositivo instável. Clique em OK para confirmar estas definições de cor. Caso contrário, estas definições serão repostas após 10 segundos."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Utiliz. da bateria"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Poupança de bateria não disponível durante o carregamento"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Poupança de bateria"</string>
diff --git a/packages/SystemUI/res/values-pt/config.xml b/packages/SystemUI/res/values-pt/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-pt/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 6feea20..199dbad 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -319,6 +321,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Limite: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Aviso de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Modo de trabalho"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Modo noturno"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Modo noturno ativado. Toque para desativar"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Modo noturno desativado. Toque para ativar"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Nenhum item recente"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Você limpou tudo"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informações do app"</string>
@@ -332,6 +337,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisão horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisão vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisão personalizada"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carregada"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Carregando"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> até concluir"</string>
@@ -436,6 +443,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> é a caixa de diálogo referente ao volume"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Toque para restaurar o original."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Você está usando seu perfil de trabalho"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Chamar"</item>
+ <item msgid="5997713001067658559">"Sistema"</item>
+ <item msgid="7858983209929864160">"Tocar"</item>
+ <item msgid="1850038478268896762">"Mídia"</item>
+ <item msgid="8265110906352372092">"Alarme"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Toque para ativar o som."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Toque para configurar para vibrar. É possível que os serviços de acessibilidade sejam silenciados."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Toque para silenciar. É possível que os serviços de acessibilidade sejam silenciados."</string>
@@ -506,21 +525,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Mais configurações"</string>
<string name="notification_done" msgid="5279426047273930175">"Concluído"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Controles de notificação do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Cor e aparência"</string>
- <string name="night_mode" msgid="3540405868248625488">"Modo noturno"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Calibrar tela"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Ativado"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Desativado"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Ativar automaticamente"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Alternar para o modo noturno conforme apropriado para o local e hora do dia"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Quando o modo noturno está ativado"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Usar o tema escuro para o SO Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Ajustar tonalidade"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Ajustar brilho"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"O tema escuro é aplicado a áreas centrais do sistema operacional Android que normalmente são exibidas em um tema claro, como as configurações."</string>
- <string name="color_apply" msgid="9212602012641034283">"Aplicar"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Confirmar configurações"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Algumas configurações de cor podem tornar o dispositivo inutilizável. Clique em \"OK\" para confirmar essas configurações de cor; caso contrário, essas configurações serão redefinidas após 10 segundos."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Uso da bateria"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"A Economia de bateria não fica disponível durante o carregamento"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Economia de bateria"</string>
diff --git a/packages/SystemUI/res/values-ro/config.xml b/packages/SystemUI/res/values-ro/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-ro/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 8c72f6a..f59721a 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -144,7 +144,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"EDGE"</string>
@@ -321,6 +323,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Limită de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Avertizare: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Modul de lucru"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Lumină de noapte"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Lumina de noapte este activată; atingeți pentru a o dezactiva"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Lumina de noapte este dezactivată; atingeți pentru a o activa"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Niciun element recent"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Ați șters tot"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informații despre aplicație"</string>
@@ -334,6 +339,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divizare pe orizontală"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divizare pe verticală"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divizare personalizată"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Încărcată"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Se încarcă"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> până la încărcare completă"</string>
@@ -438,6 +445,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> afișează caseta de dialog pentru volum"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Atingeți pentru a restabili versiunea originală."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Acum folosiți profilul de serviciu"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Apel"</item>
+ <item msgid="5997713001067658559">"Sistem"</item>
+ <item msgid="7858983209929864160">"Sonerie"</item>
+ <item msgid="1850038478268896762">"Conținut media"</item>
+ <item msgid="8265110906352372092">"Alarmă"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Atingeți pentru a activa sunetul."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Atingeți pentru a seta vibrarea. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Atingeți pentru a dezactiva sunetul. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string>
@@ -508,21 +527,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Mai multe setări"</string>
<string name="notification_done" msgid="5279426047273930175">"Terminat"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Opțiuni privind notificările pentru <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Culoare și aspect"</string>
- <string name="night_mode" msgid="3540405868248625488">"Modul Noapte"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Calibrați afișarea"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Activat"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Dezactivat"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Activați automat"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Comutați la modul Noapte în funcție de locație și de momentul zilei"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Când modul Noapte este activat"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Folosiți tema întunecată pentru SO Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Ajustați culoarea"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Ajustați luminozitatea"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Tema întunecată se aplică zonelor principale ale sistemului de operare Android care sunt de obicei afișate cu o temă deschisă la culoare, cum ar fi Setările."</string>
- <string name="color_apply" msgid="9212602012641034283">"Aplicați"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Confirmați setările"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Unele setări pentru culori pot face dispozitivul să nu mai funcționeze. Dați clic pe OK pentru a confirma aceste setări pentru culori. În caz contrar, acestea se vor reseta după 10 secunde."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Utilizarea bateriei"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Economisirea bateriei nu este disponibilă pe durata încărcării"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Economisirea bateriei"</string>
diff --git a/packages/SystemUI/res/values-ru/config.xml b/packages/SystemUI/res/values-ru/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-ru/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 062ea48..bee7eed 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -145,7 +145,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3,5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Роуминг"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -323,6 +325,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Ограничение: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Предупреждение: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Рабочий режим"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Ночной режим"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Ночной режим включен. Нажмите, чтобы отключить."</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Ночной режим отключен. Нажмите, чтобы включить."</string>
<string name="recents_empty_message" msgid="808480104164008572">"Недавних приложений нет"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Вы очистили всё"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Сведения о приложении"</string>
@@ -336,6 +341,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Разделить по горизонтали"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Разделить по вертикали"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Разделить по-другому"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Батарея заряжена"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Зарядка батареи"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> до полной зарядки"</string>
@@ -440,6 +447,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"Приложение <xliff:g id="APP_NAME">%1$s</xliff:g> назначено регулятором громкости"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Нажмите, чтобы восстановить оригинал"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Вы перешли в рабочий профиль"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Вызов"</item>
+ <item msgid="5997713001067658559">"Система"</item>
+ <item msgid="7858983209929864160">"Рингтон"</item>
+ <item msgid="1850038478268896762">"Мультимедиа"</item>
+ <item msgid="8265110906352372092">"Будильник"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Нажмите, чтобы включить звук."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Нажмите, чтобы включить вибрацию. Специальные возможности могут прекратить работу."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Нажмите, чтобы выключить звук. Специальные возможности могут прекратить работу."</string>
@@ -510,21 +529,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Другие настройки"</string>
<string name="notification_done" msgid="5279426047273930175">"Готово"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Управление уведомлениями (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Цвета и стиль"</string>
- <string name="night_mode" msgid="3540405868248625488">"Ночной режим"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Калибровка дисплея"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Включен"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Отключен"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Включать автоматически"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Включать ночной режим с учетом местоположения и времени суток"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"В ночном режиме"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Использовать темное оформление для Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Изменять оттенок"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Яркость"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Темное оформление применяется к основным элементам системы Android (таким, как приложение \"Настройки\"), которые обычно показываются в светлом."</string>
- <string name="color_apply" msgid="9212602012641034283">"Применить"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Подтвердите настройки"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Некоторые цветовые настройки могут затруднить работу с устройством. Чтобы применить выбранные параметры, нажмите \"ОК\". В противном случае они будут сброшены через 10 секунд."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Уровень заряда"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Режим энергосбережения нельзя включить во время зарядки"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Режим энергосбережения"</string>
diff --git a/packages/SystemUI/res/values-si-rLK/config.xml b/packages/SystemUI/res/values-si-rLK/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-si-rLK/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index 90f75a6..0811f5e 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"රෝමිං"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> සීමිත"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> අවවාද කිරීම"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"වැඩ ප්රකාරය"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"රාත්රී ආලෝකය"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"රාත්රී ආලෝකය ක්රියාත්මකයි, ක්රියාවිරහිත කිරීමට තට්ටු කරන්න"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"රාත්රී ආලෝකය ක්රියාවිරහිතයි, ක්රියාත්මක කිරීමට තට්ටු කරන්න"</string>
<string name="recents_empty_message" msgid="808480104164008572">"මෑත අයිතම නැත"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"ඔබ සියලු දේ හිස් කර ඇත"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"යෙදුම් තොරතුරු"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"තිරස්ව වෙන් කරන්න"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"සිරස්ව වෙන් කරන්න"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"අභිමත ලෙස වෙන් කරන්න"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"අරෝපිතයි"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"ආරෝපණය වෙමින්"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> සම්පූර්ණ වන තෙක්"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ධාරිතා සංවාදයයි"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"මුල් තත්ත්වය නැවත ප්රතිසාධනය කිරීමට තට්ටු කරන්න."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"ඔබ ඔබේ කාර්යාල පැතිකඩ භාවිත කරමින් සිටී"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"ඇමතුම"</item>
+ <item msgid="5997713001067658559">"පද්ධතිය"</item>
+ <item msgid="7858983209929864160">"නාද කරන්න"</item>
+ <item msgid="1850038478268896762">"මාධ්ය"</item>
+ <item msgid="8265110906352372092">"එලාමය"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"බ්ලූටූත්"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. නිහඬ කිරීම ඉවත් කිරීමට තට්ටු කරන්න."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. කම්පනය කිරීමට තට්ටු කරන්න. ප්රවේශ්යතා සේවා නිහඬ කළ හැකිය."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. නිහඬ කිරීමට තට්ටු කරන්න. ප්රවේශ්යතා සේවා නිහඬ කළ හැකිය."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"තව සැකසීම්"</string>
<string name="notification_done" msgid="5279426047273930175">"නිමයි"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> දැනුම්දීම් පාලන"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"වර්ණය සහ පෙනුම"</string>
- <string name="night_mode" msgid="3540405868248625488">"රාත්රී ප්රකාරය"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"සංදර්ශකය ක්රමාංකනය කරන්න"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"ක්රියාත්මකයි"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"ක්රියාවිරහිතයි"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"ස්වයංක්රියව ක්රියාත්මක කරන්න"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"ස්ථානය සහ දවසේ වේලාවට ගැළපෙන ලෙස රාත්රී ප්රකාරයට මාරු වන්න"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"රාත්රී ප්රකාරය ක්රියාත්මක විට"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android OS සඳහා අඳුරු තේමාව භාවිත කරන්න"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"පැහැය සීරුමාරු කරන්න"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"දීප්තිය සීරුමාරු කරන්න"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"සැකසීම් වැනි, සාමාන්යයෙන් ලා පැහැ තේමාවක සංදර්ශනය වන Android OS හි මූලික ප්රදේශවලට අඳුරු තේමාව යෙදේ."</string>
- <string name="color_apply" msgid="9212602012641034283">"යොදන්න"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"සැකසීම් තහවුරු කරන්න"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"සමහර වර්ණ සැකසීම් මෙම උපාංගය භාවිත කළ නොහැකි තත්ත්වයට පත් කළ හැකිය. මෙම වර්ණ සැකසීම් තහවුරු කිරීමට හරි ක්ලික් කරන්න, නැතහොත් මෙම සැකසීම් තත්පර 10කට පසුව යළි සකසනු ඇත."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"බැටරි භාවිතය"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ආරෝපණය අතරතුර බැටරි සුරැකුම ලබා ගත නොහැකිය."</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"බැටරි සුරැකුම"</string>
diff --git a/packages/SystemUI/res/values-sk/config.xml b/packages/SystemUI/res/values-sk/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-sk/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index f0537c3..304c9a0 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -145,7 +145,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -323,10 +325,13 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Limit: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Upozornenie pri <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Pracovný režim"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Nočný režim"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Nočný režim je zapnutý (vypnete ho klepnutím)"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Nočný režim je vypnutý (zapnete ho klepnutím)"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Žiadne nedávne položky"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Vymazali ste všetko"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informácie o aplikácii"</string>
- <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"pripnutie k obrazovke"</string>
+ <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"pripnutie obrazovky"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"hľadať"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikáciu <xliff:g id="APP">%s</xliff:g> sa nepodarilo spustiť"</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Aplikácia <xliff:g id="APP">%s</xliff:g> je v núdzovom režime zakázaná."</string>
@@ -336,6 +341,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Rozdeliť vodorovné"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Rozdeliť zvislé"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Rozdeliť vlastné"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Nabitá"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Nabíja sa"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Úplné nabitie o <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
@@ -440,6 +447,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> je dialóg hlasitosti"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Klepnutím obnovíte pôvodnú verziu."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Používate svoj pracovný profil."</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Hovor"</item>
+ <item msgid="5997713001067658559">"Systém"</item>
+ <item msgid="7858983209929864160">"Zvonenie"</item>
+ <item msgid="1850038478268896762">"Médiá"</item>
+ <item msgid="8265110906352372092">"Budík"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Klepnutím zapnite zvuk."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Klepnutím aktivujte režim vibrovania. Služby dostupnosti je možné stlmiť."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Klepnutím vypnite zvuk. Služby dostupnosti je možné stlmiť."</string>
@@ -510,21 +529,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Ďalšie nastavenia"</string>
<string name="notification_done" msgid="5279426047273930175">"Hotovo"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Ovládacie prvky pre upozornenia z aplikácie <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Farba a vzhľad"</string>
- <string name="night_mode" msgid="3540405868248625488">"Nočný režim"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Kalibrovať obrazovku"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Zapnutý"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Vypnutý"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Zapínať automaticky"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Prepnúť do Nočného režimu podľa miesta a času dňa"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Keď je zapnutý Nočný režim"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Použiť tmavý motív pre systém Android OS"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Upraviť tónovanie"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Upraviť jas"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"V hlavných oblastiach systému Android OS (ako sú Nastavenia), ktoré sú obyčajne zobrazené vo svetlom motíve, je použitý tmavý motív."</string>
- <string name="color_apply" msgid="9212602012641034283">"Použiť"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Potvrdenie nastavení"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Niektoré nastavenia farieb môžu toto zariadenie znefunkčniť. Tieto nastavenia farieb potvrdíte kliknutím na tlačidlo OK, ináč sa tieto nastavenia o 10 sekúnd obnovia."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Využitie batérie"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Počas nabíjania nie je Šetrič batérie k dispozícii"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Šetrič batérie"</string>
@@ -591,12 +595,12 @@
<string name="end" msgid="125797972524818282">"Koniec"</string>
<string name="space" msgid="804232271282109749">"Medzerník"</string>
<string name="menu_ime" msgid="4943221416525250684">"Prepínač – ponuka/klávesnica"</string>
- <string name="select_button" msgid="1597989540662710653">"Výber tlačidla, ktoré sa má pridať"</string>
+ <string name="select_button" msgid="1597989540662710653">"Výber tlačidla"</string>
<string name="add_button" msgid="4134946063432258161">"Pridať tlačidlo"</string>
<string name="save" msgid="2311877285724540644">"Uložiť"</string>
<string name="reset" msgid="2448168080964209908">"Obnoviť"</string>
- <string name="no_home_title" msgid="1563808595146071549">"Tlačidlo Plocha sa nenašlo"</string>
- <string name="no_home_message" msgid="5408485011659260911">"Ak chcete používať navigáciu v tomto zariadení, musíte použiť tlačidlo Plocha. Pred uložením pridajte tlačidlo Plocha."</string>
+ <string name="no_home_title" msgid="1563808595146071549">"Nenašlo sa tlačidlo plochy"</string>
+ <string name="no_home_message" msgid="5408485011659260911">"Ak chcete používať navigáciu v tomto zariadení, musíte použiť tlačidlo plochy. Pred uložením pridajte tlačidlo plochy."</string>
<string name="adjust_button_width" msgid="6138616087197632947">"Upraviť šírku tlačidla"</string>
<string name="clipboard" msgid="1313879395099896312">"Schránka"</string>
<string name="clipboard_description" msgid="3819919243940546364">"Schránka umožňuje presunúť položky priamo do schránky. Ak ju máte k dispozícii, môžete ich z nej aj priamo vytiahnuť."</string>
diff --git a/packages/SystemUI/res/values-sl/config.xml b/packages/SystemUI/res/values-sl/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-sl/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 4eafc0b..a8bc3d1 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -145,7 +145,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Gostovanje"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -323,6 +325,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Omejitev: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Opozorilo – <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Način za delo"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Nočna svetloba"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Nočna svetloba je vklopljena. Dotaknite se, če jo želite izklopiti."</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Nočna svetloba je izklopljena. Dotaknite se, če jo želite vklopiti."</string>
<string name="recents_empty_message" msgid="808480104164008572">"Ni nedavnih elementov"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Vse te počistili"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Podatki o aplikaciji"</string>
@@ -336,6 +341,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Razdeli vodoravno"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Razdeli navpično"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Razdeli po meri"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Akumulator napolnjen"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Polnjenje"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> do napolnjenosti"</string>
@@ -440,6 +447,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> je pogovorno okno glede prostornine"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Dotaknite se, če želite obnoviti prvotno stanje."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Uporabljate delovni profil"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Klic"</item>
+ <item msgid="5997713001067658559">"Sistem"</item>
+ <item msgid="7858983209929864160">"Zvonjenje"</item>
+ <item msgid="1850038478268896762">"Predstavnost"</item>
+ <item msgid="8265110906352372092">"Alarm"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Dotaknite se, če želite vklopiti zvok."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Dotaknite se, če želite nastaviti vibriranje. V storitvah za ljudi s posebnimi potrebami bo morda izklopljen zvok."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Dotaknite se, če želite izklopiti zvok. V storitvah za ljudi s posebnimi potrebami bo morda izklopljen zvok."</string>
@@ -510,21 +529,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Več nastavitev"</string>
<string name="notification_done" msgid="5279426047273930175">"Dokončano"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Kontrolniki obvestil za aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Barva in videz"</string>
- <string name="night_mode" msgid="3540405868248625488">"Nočni način"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Umerjanje zaslona"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Vklopljeno"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Izklopljeno"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Samodejni vklop"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Preklop v nočni način, kot je ustrezno glede na lokacijo in uro v dnevu"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Ko je vklopljen nočni način"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Uporaba temne teme za sistem Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Prilagodi odtenek"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Prilagodi svetlost"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Za osrednja področja sistema Android, ki so običajno prikazana v svetli temi, na primer nastavitve, je uporabljena temna tema."</string>
- <string name="color_apply" msgid="9212602012641034283">"Uporabi"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Potrditev nastavitev"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Zaradi nekaterih barvnih nastavitev lahko postane ta naprava neuporabna. Kliknite »V redu«, če želite potrditi te barvne nastavitve. V nasprotnem primeru se bodo čez 10 sekund ponastavile na prvotno vrednost."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Poraba akumulatorja"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Varčevanje z energijo akumulatorja med polnjenjem ni na voljo"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Varčevanje z energijo akumulatorja"</string>
diff --git a/packages/SystemUI/res/values-sq-rAL/config.xml b/packages/SystemUI/res/values-sq-rAL/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-sq-rAL/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-sq-rAL/strings.xml b/packages/SystemUI/res/values-sq-rAL/strings.xml
index 688e643..4bc29aa 100644
--- a/packages/SystemUI/res/values-sq-rAL/strings.xml
+++ b/packages/SystemUI/res/values-sq-rAL/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"Lidhje CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Kufiri: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Paralajmërim për kufirin prej <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Modaliteti i punës"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Drita e natës"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Drita e natës është joaktive, trokit për ta çaktivizuar"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Drita e natës është joaktive, trokit për ta aktivizuar"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Nuk ka asnjë artikull të fundit"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"I ke pastruar të gjitha"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacioni i aplikacionit"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Ndaje horizontalisht"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Ndaj vertikalisht"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Ndaj të personalizuarën"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"I ngarkuar"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Po ngarkohet"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> deri sa të mbushet"</string>
@@ -344,8 +351,7 @@
<string name="zen_silence_introduction" msgid="3137882381093271568">"Kjo bllokon TË GJITHË tingujt dhe dridhjet, duke përfshirë edhe nga alarmet, muzika, videot dhe lojërat."</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Njoftimet më pak urgjente, më poshtë!"</string>
- <!-- no translation found for notification_tap_again (7590196980943943842) -->
- <skip />
+ <string name="notification_tap_again" msgid="7590196980943943842">"Trokit përsëri për ta hapur"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Rrëshqit për të shkyçur"</string>
<string name="phone_hint" msgid="4872890986869209950">"Rrëshqit për të hapur telefonin"</string>
<string name="voice_hint" msgid="8939888732119726665">"Rrëshqit për të hapur ndihmën zanore"</string>
@@ -435,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> është dialogu i volumit"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Trokit për të restauruar origjinalin."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Po përdor profilin tënd të punës"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Telefono"</item>
+ <item msgid="5997713001067658559">"Sistemi"</item>
+ <item msgid="7858983209929864160">"Bjeri ziles"</item>
+ <item msgid="1850038478268896762">"Media"</item>
+ <item msgid="8265110906352372092">"Alarmi"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Trokit për të aktivizuar."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Trokit për ta caktuar te dridhja. Shërbimet e qasshmërisë mund të çaktivizohen."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Trokit për të çaktivizuar. Shërbimet e qasshmërisë mund të çaktivizohen."</string>
@@ -505,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Cilësime të tjera"</string>
<string name="notification_done" msgid="5279426047273930175">"U krye"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Kontrollet e njoftimeve të <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Ngjyra dhe pamja"</string>
- <string name="night_mode" msgid="3540405868248625488">"Modaliteti i natës"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Kalibro ekranin"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Aktiv"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Joaktiv"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Aktivizoje automatikisht"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Kalo në \"Modalitetin e natës\" sipas përshtatshmërisë për vendin dhe kohën e ditës"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Kur \"Modaliteti i natës\" është aktiv"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Përdor temën e errët për Android OS"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Rregullo nuancën"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Rregullo ndriçimin"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Tema e errët zbatohet në zonat kryesore të Android OS që shfaqen zakonisht në një temë të çelur, siç janë \"Cilësimet\"."</string>
- <string name="color_apply" msgid="9212602012641034283">"Zbato"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Konfirmo cilësimet"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Disa cilësime ngjyrash mund ta bëjnë këtë pajisje të papërdorshme. Kliko OK për të konfirmuar këto cilësime ngjyrash, përndryshe këto cilësime do të rivendosen pas 10 sekondash."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Përdorimi i baterisë"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"\"Kursyesi i baterisë\" nuk është i disponueshëm gjatë karikimit"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Kursyesi i baterisë"</string>
diff --git a/packages/SystemUI/res/values-sr/config.xml b/packages/SystemUI/res/values-sr/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-sr/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 2bfdc88..16ae7e2 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -144,7 +144,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Роминг"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -319,6 +321,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Ограничење од <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Упозорење за <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Режим рада"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Ноћно светло"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Ноћно светло је укључено, додирните да бисте га искључили"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Ноћно светло је искључено, додирните да бисте га укључили"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Нема недавних ставки"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Обрисали сте све"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Информације о апликацији"</string>
@@ -332,6 +337,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Подели хоризонтално"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Подели вертикално"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Прилагођено дељење"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Напуњена је"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Пуњење"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> док се не напуни"</string>
@@ -436,6 +443,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> је дијалог за јачину звука"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Додирните да бисте вратили оригинал."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Користите профил за Work"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Позови"</item>
+ <item msgid="5997713001067658559">"Систем"</item>
+ <item msgid="7858983209929864160">"Прстен"</item>
+ <item msgid="1850038478268896762">"Медијуми"</item>
+ <item msgid="8265110906352372092">"Аларм"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Додирните да бисте укључили звук."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Додирните да бисте подесили на вибрацију. Звук услуга приступачности ће можда бити искључен."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Додирните да бисте искључили звук. Звук услуга приступачности ће можда бити искључен."</string>
@@ -506,21 +525,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Још подешавања"</string>
<string name="notification_done" msgid="5279426047273930175">"Готово"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Контроле обавештења за апликацију <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Боја и изглед"</string>
- <string name="night_mode" msgid="3540405868248625488">"Ноћни режим"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Калибришите екран"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Укључено"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Искључено"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Аутоматски укључи"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Пређите на ноћни режим у зависности од локације и доба дана"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Када је ноћни режим укључен"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Користи тамну тему за Android ОС"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Прилагоди сенку"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Прилагоди осветљеност"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Тамна тема се примењује на кључне делове Android ОС-а који се обично приказују у светлој теми, попут Подешавања."</string>
- <string name="color_apply" msgid="9212602012641034283">"Примени"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Потврдите подешавања"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Нека подешавања боја могу да учине уређај неупотребљивим. Кликните на Потврди да бисте потврдили ова подешавања боја, пошто ће се у супротном ова подешавања ресетовати након 10 секунди."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Потрошња батерије"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Уштеда батерије није доступна током пуњења"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Уштеда батерије"</string>
diff --git a/packages/SystemUI/res/values-sv/config.xml b/packages/SystemUI/res/values-sv/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-sv/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 7c2f3af..e6988d7 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Gräns: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Varning <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Arbetsläge"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Nattljus"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Nattljus har aktiverats. Tryck för att inaktivera"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Nattljus har inaktiverats. Tryck för att aktivera"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Listan med de senaste åtgärderna är tom"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Du har tömt listan"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Appinformation"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Dela horisontellt"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Dela vertikalt"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Dela anpassad"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Laddat"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Laddar"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> tills batteriet är fulladdat"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> används som volymkontroll"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Återställ originalet genom att trycka här."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Du använder din jobbprofil"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Ring"</item>
+ <item msgid="5997713001067658559">"System"</item>
+ <item msgid="7858983209929864160">"Ring"</item>
+ <item msgid="1850038478268896762">"Media"</item>
+ <item msgid="8265110906352372092">"Alarm"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Tryck här om du vill slå på ljudet."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tryck här om du vill sätta på vibrationen. Tillgänglighetstjänster kanske inaktiveras."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Tryck här om du vill stänga av ljudet. Tillgänglighetstjänsterna kanske inaktiveras."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Fler inställningar"</string>
<string name="notification_done" msgid="5279426047273930175">"Klar"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Inställningar för <xliff:g id="APP_NAME">%1$s</xliff:g>-aviseringar"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Färg och utseende"</string>
- <string name="night_mode" msgid="3540405868248625488">"Nattläge"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Kalibrera skärmen"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Aktiverat"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Inaktiverat"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Aktivera automatiskt"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Byt till Nattläge vid passande platser och tider på dygnet"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"När Nattläget är aktiverat"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Använd mörkt tema för Android OS"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Justera ton"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Justera ljusstyrka"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Det mörka temat används för kärnfunktioner i Android OS som brukar visas med ett ljust tema, t.ex. inställningarna."</string>
- <string name="color_apply" msgid="9212602012641034283">"Verkställ"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Bekräfta inställningarna"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Vissa färginställningar kan göra den här enheten oanvändbar. Klicka på OK om du vill bekräfta färginställningarna, annars återställs inställningarna efter 10 sekunder."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Batteriförbrukning"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Batterisparläget är inte tillgängligt vid laddning"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Batterisparläge"</string>
diff --git a/packages/SystemUI/res/values-sw/config.xml b/packages/SystemUI/res/values-sw/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-sw/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 28b6153..50c6685 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Inatumia data nje mtandao wako wa kawaida"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Ukingo"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"kikomo <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Onyo <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Hali ya kazi"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Mwanga wa Usiku"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Umewasha hali ya Mwanga wa Usiku, gonga ili uizime"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Umezima hali ya Mwanga wa Usiku, gonga ili uiwashe"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Hakuna vipengee vya hivi karibuni"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Umeondoa vipengee vyote"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Maelezo ya Programu"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Gawanya Mlalo"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Gawanya Wima"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Maalum Iliyogawanywa"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Betri imejaa"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Inachaji"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Imebakisha <xliff:g id="CHARGING_TIME">%s</xliff:g> ijae"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ni mazungumzo ya sauti"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Gonga ili urejeshe picha ya asili."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Unatumia wasifu wako wa kazini"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Piga simu"</item>
+ <item msgid="5997713001067658559">"Mfumo"</item>
+ <item msgid="7858983209929864160">"Pete"</item>
+ <item msgid="1850038478268896762">"Media"</item>
+ <item msgid="8265110906352372092">"Kengele"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Gonga ili urejeshe."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Gonga ili uweke mtetemo. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Gonga ili ukomeshe. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Mipangilio zaidi"</string>
<string name="notification_done" msgid="5279426047273930175">"Nimemaliza"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Vidhibiti vya arifa za <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Rangi na mwonekano"</string>
- <string name="night_mode" msgid="3540405868248625488">"Hali ya usiku"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Rekebisha onyesho"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Imewashwa"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Imezimwa"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Washa kiotomatiki"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Badilisha kuwa Hali ya Usiku kulingana na mahali na wakati"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Hali ya Usiku inapowashwa"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Tumia mandhari ya giza katika Mfumo wa Uendeshaji wa Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Rekebisha kivulivuli"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Rekebisha mwangaza"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Mandhari yenye giza yametumika katika maeneo muhimu ya Mfumo wa Uendeshaji wa Android ambayo kwa kawaida huonyeshwa katika mandhari yenye mwangaza, kama vile Mipangilio."</string>
- <string name="color_apply" msgid="9212602012641034283">"Tumia"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Thibitisha mipangilio"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Baadhi ya mipangilio ya rangi inaweza kufanya kifaa hiki kisitumike. Bofya Sawa ili uthibitishe mipangilio hii ya rangi, vinginevyo, mipangilio hii itajiweka upya baada ya sekunde 10."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Matumizi ya betri"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Kiokoa Betri hakipatikani unapochaji betri"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Kiokoa Betri"</string>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 6c5a313..7e63cbf 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -94,8 +94,6 @@
<dimen name="navigation_key_width">128dp</dimen>
<dimen name="navigation_key_padding">25dp</dimen>
- <dimen name="qs_expand_margin">0dp</dimen>
-
<!-- Keyboard shortcuts helper -->
<dimen name="ksh_layout_width">488dp</dimen>
diff --git a/packages/SystemUI/res/values-ta-rIN/config.xml b/packages/SystemUI/res/values-ta-rIN/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-ta-rIN/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml
index d7ba719..f0cb530 100644
--- a/packages/SystemUI/res/values-ta-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ta-rIN/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"ரோமிங்"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> வரம்பு"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> எச்சரிக்கை"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"பணிப் பயன்முறை"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"இரவு ஒளி"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"இரவு ஒளி இயக்கப்பட்டுள்ளது. முடக்க, தட்டவும்"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"இரவு ஒளி முடக்கப்பட்டுள்ளது. இயக்க, தட்டவும்"</string>
<string name="recents_empty_message" msgid="808480104164008572">"சமீபத்திய பணிகள் இல்லை"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"எல்லாவற்றையும் அழித்துவிட்டீர்கள்"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"பயன்பாட்டு தகவல்"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"கிடைமட்டமாகப் பிரி"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"செங்குத்தாகப் பிரி"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"தனிவிருப்பத்தில் பிரி"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"சார்ஜ் செய்யப்பட்டது"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"சார்ஜாகிறது"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"முழுவதும் சார்ஜாக <xliff:g id="CHARGING_TIME">%s</xliff:g> ஆகும்"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"ஒலியளவு செய்தி: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"அசலை மீட்டமைக்க, தட்டவும்."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"பணி சுயவிவரத்தைப் பயன்படுத்துகிறீர்கள்"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"அழைப்பு"</item>
+ <item msgid="5997713001067658559">"சாதனம்"</item>
+ <item msgid="7858983209929864160">"ரிங்"</item>
+ <item msgid="1850038478268896762">"மீடியா"</item>
+ <item msgid="8265110906352372092">"அலாரம்"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"புளூடூத்"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. ஒலி இயக்க, தட்டவும்."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. அதிர்விற்கு அமைக்க, தட்டவும். அணுகல்தன்மை சேவைகள் ஒலியடக்கப்படக்கூடும்."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. ஒலியடக்க, தட்டவும். அணுகல்தன்மை சேவைகள் ஒலியடக்கப்படக்கூடும்."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"மேலும் அமைப்புகள்"</string>
<string name="notification_done" msgid="5279426047273930175">"முடிந்தது"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> அறிவிப்புக் கட்டுப்பாடுகள்"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"வண்ணமும் தோற்றமும்"</string>
- <string name="night_mode" msgid="3540405868248625488">"இரவுப் பயன்முறை"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"திரையை அளவுத்திருத்தம் செய்"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"இயக்கத்தில்"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"முடக்கத்தில்"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"தானாகவே இயக்கு"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"இருப்பிடம் மற்றும் நேரத்தின்படி இரவுப் பயன்முறைக்கு மாற்று"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"இரவுப் பயன்முறை இயக்கப்பட்டிருக்கும் போது"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android OSக்காக அடர் தீமினைப் பயன்படுத்து"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"டிண்ட்டைச் சரிசெய்"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"ஒளிர்வைச் சரிசெய்"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"வழக்கமாக வெளிர் தீமில் காட்டப்படுகிற Android OS இன் முக்கிய பகுதிகளில் (எ.கா. அமைப்புகள்) அடர் தீம் பயன்படுத்தப்படுகிறது."</string>
- <string name="color_apply" msgid="9212602012641034283">"பயன்படுத்து"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"அமைப்புகளை உறுதிப்படுத்து"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"சில வண்ண அமைப்புகள் இந்தச் சாதனத்தைப் பயன்படுத்த முடியாதபடி செய்யலாம். இந்த வண்ண அமைப்புகளை உறுதிப்படுத்த, சரி என்பதைக் கிளிக் செய்யவும், இல்லையெனில் இந்த அமைப்புகள் 10 வினாடிகளுக்குப் பின் மீட்டமைக்கப்படும்."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"பேட்டரி உபயோகம்"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"சார்ஜ் செய்யும் போது பேட்டரி சேமிப்பானைப் பயன்படுத்த முடியாது"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"பேட்டரி சேமிப்பான்"</string>
diff --git a/packages/SystemUI/res/values-te-rIN/config.xml b/packages/SystemUI/res/values-te-rIN/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-te-rIN/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml
index 1843ee4..e50d47a 100644
--- a/packages/SystemUI/res/values-te-rIN/strings.xml
+++ b/packages/SystemUI/res/values-te-rIN/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"రోమింగ్"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"ఎడ్జ్"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> పరిమితి"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> హెచ్చరిక"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"పని మోడ్"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"రాత్రి కాంతి"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"రాత్రి కాంతి ఆన్లో ఉంది, ఆఫ్ చేయడానికి నొక్కండి"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"రాత్రి కాంతి ఆఫ్లో ఉంది, ఆన్ చేయడానికి నొక్కండి"</string>
<string name="recents_empty_message" msgid="808480104164008572">"ఇటీవలి అంశాలు ఏవీ లేవు"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"మీరు అన్నింటినీ తీసివేసారు"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"అనువర్తన సమాచారం"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"సమతలంగా విభజించు"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"లంబంగా విభజించు"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"అనుకూలంగా విభజించు"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"ఛార్జ్ చేయబడింది"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"ఛార్జ్ అవుతోంది"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"పూర్తిగా నిండటానికి <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> అనేది వాల్యూమ్ డైలాగ్"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"అసలు దాన్ని పునరుద్ధరించడానికి నొక్కండి."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"మీరు మీ కార్యాలయ ప్రొఫైల్ను ఉపయోగిస్తున్నారు"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"కాల్"</item>
+ <item msgid="5997713001067658559">"సిస్టమ్"</item>
+ <item msgid="7858983209929864160">"రింగ్"</item>
+ <item msgid="1850038478268896762">"మీడియా"</item>
+ <item msgid="8265110906352372092">"అలారం"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"బ్లూటూత్"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. అన్మ్యూట్ చేయడానికి నొక్కండి."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. వైబ్రేషన్కు సెట్ చేయడానికి నొక్కండి. ప్రాప్యత సేవలు మ్యూట్ చేయబడవచ్చు."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. మ్యూట్ చేయడానికి నొక్కండి. ప్రాప్యత సేవలు మ్యూట్ చేయబడవచ్చు."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"మరిన్ని సెట్టింగ్లు"</string>
<string name="notification_done" msgid="5279426047273930175">"పూర్తయింది"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> నోటిఫికేషన్ నియంత్రణలు"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"రంగు మరియు కనిపించే తీరు"</string>
- <string name="night_mode" msgid="3540405868248625488">"రాత్రి మోడ్"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"డిస్ప్లేని క్రమాంకనం చేయండి"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"ఆన్లో ఉంది"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"ఆఫ్లో ఉంది"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"స్వయంచాలకంగా ఆన్ చేయి"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"స్థానం మరియు రోజులో సమయానికి తగినట్లుగా రాత్రి మోడ్కి మారుస్తుంది"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"రాత్రి మోడ్ ఆన్లో ఉన్నప్పుడు"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android OS కోసం ముదురు రంగు థీమ్ ఉపయోగించండి"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"లేత రంగును సర్దుబాటు చేయండి"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"ప్రకాశాన్ని సర్దుబాటు చేయండి"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"సాధారణంగా లేత రంగు థీమ్లో ప్రదర్శించబడే సెట్టింగ్ల వంటి Android OS ప్రధాన అంశాలకు ముదురు రంగు థీమ్ వర్తింపజేయబడుతుంది."</string>
- <string name="color_apply" msgid="9212602012641034283">"వర్తింపజేయి"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"సెట్టింగ్లను నిర్ధారించండి"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"కొన్ని రంగు సెట్టింగ్ల వలన ఈ పరికరం ఉపయోగించలేని విధంగా అయిపోవచ్చు. ఈ రంగు సెట్టింగ్లను నిర్ధారించడానికి సరే క్లిక్ చేయండి లేదంటే ఈ సెట్టింగ్లు 10 సెకన్ల తర్వాత రీసెట్ చేయబడతాయి."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"బ్యాటరీ వినియోగం"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ఛార్జ్ అవుతున్న సమయంలో బ్యాటరీ సేవర్ అందుబాటులో ఉండదు"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"బ్యాటరీ సేవర్"</string>
diff --git a/packages/SystemUI/res/values-th/config.xml b/packages/SystemUI/res/values-th/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-th/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index a20033a..7eb227e 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"โรมมิ่ง"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"EDGE"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"ขีดจำกัด <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"คำเตือน <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"โหมดการทำงาน"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"แสงตอนกลางคืน"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"แสงตอนกลางคืนเปิดอยู่ แตะเพื่อปิด"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"แสงตอนกลางคืนปิดอยู่ แตะเพื่อเปิด"</string>
<string name="recents_empty_message" msgid="808480104164008572">"ไม่มีรายการล่าสุด"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"คุณได้ล้างทุกอย่างแล้ว"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"ข้อมูลแอปพลิเคชัน"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"แยกในแนวนอน"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"แยกในแนวตั้ง"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"แยกแบบกำหนดเอง"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"ชาร์จแล้ว"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"กำลังชาร์จ"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"อีก <xliff:g id="CHARGING_TIME">%s</xliff:g> จึงจะเต็ม"</string>
@@ -338,7 +345,7 @@
<string name="description_target_search" msgid="3091587249776033139">"ค้นหา"</string>
<string name="description_direction_up" msgid="7169032478259485180">"เลื่อนขึ้นเพื่อ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
<string name="description_direction_left" msgid="7207478719805562165">"เลื่อนไปทางซ้ายเพื่อ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
- <string name="zen_priority_introduction" msgid="3070506961866919502">"คุณจะไม่ถูกรบกวนจากเสียงและการสั่น ยกเว้นการปลุก การเตือนความจำ กิจกรรม และผู้โทรที่คุณระบุ"</string>
+ <string name="zen_priority_introduction" msgid="3070506961866919502">"คุณจะไม่ถูกรบกวนจากเสียงและการสั่น ยกเว้นการปลุก การช่วยเตือน กิจกรรม และผู้โทรที่คุณระบุ"</string>
<string name="zen_priority_customize_button" msgid="7948043278226955063">"กำหนดค่า"</string>
<string name="zen_silence_introduction_voice" msgid="2284540992298200729">"การใช้โหมดนี้จะบล็อกเสียงและการสั่นทั้งหมด ซึ่งรวมถึงเสียงปลุก เพลง วิดีโอ และเกม คุณจะยังโทรออกได้อยู่"</string>
<string name="zen_silence_introduction" msgid="3137882381093271568">"การใช้โหมดนี้จะบล็อกเสียงและการสั่นทั้งหมด ซึ่งรวมถึงเสียงปลุก เพลง วิดีโอ และเกม"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> เป็นช่องโต้ตอบระดับเสียง"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"แตะเพื่อคืนค่าเป็นค่าเดิม"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"คุณกำลังใช้โปรไฟล์งานของคุณ"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"โทร"</item>
+ <item msgid="5997713001067658559">"ระบบ"</item>
+ <item msgid="7858983209929864160">"ทำให้ส่งเสียง"</item>
+ <item msgid="1850038478268896762">"สื่อ"</item>
+ <item msgid="8265110906352372092">"การปลุก"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"บลูทูธ"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s แตะเพื่อเปิดเสียง"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s แตะเพื่อตั้งค่าให้สั่น อาจมีการปิดเสียงบริการการเข้าถึง"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s แตะเพื่อปิดเสียง อาจมีการปิดเสียงบริการการเข้าถึง"</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"การตั้งค่าเพิ่มเติม"</string>
<string name="notification_done" msgid="5279426047273930175">"เสร็จสิ้น"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"ส่วนควบคุมการแจ้งเตือนของ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"สีและลักษณะที่ปรากฏ"</string>
- <string name="night_mode" msgid="3540405868248625488">"โหมดกลางคืน"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"ปรับเทียบการแสดงผล"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"เปิด"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"ปิด"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"เปิดอัตโนมัติ"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"เปลี่ยนเป็นโหมดกลางคืนตามความเหมาะสมกับสถานที่และเวลาของวัน"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"เมื่อเปิดโหมดกลางคืน"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"ใช้ธีมสีเข้มสำหรับ Android OS"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"ปรับการแต้มสี"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"ปรับความสว่าง"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"ใช้ธีมสีเข้มในบริเวณสำคัญของระบบปฏิบัติการ Android ซึ่งปกติแล้วจะแสดงด้วยธีมสีอ่อน เช่น การตั้งค่า"</string>
- <string name="color_apply" msgid="9212602012641034283">"ใช้"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"ยืนยันการตั้งค่า"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"การตั้งค่าสีบางอย่างอาจทำให้อุปกรณ์นี้ใช้งานไม่ได้ คลิกตกลงเพื่อยืนยันการตั้งค่าสีเหล่านี้ มิฉะนั้นระบบจะรีเซ็ตการตั้งค่าหลังจาก 10 วินาที"</string>
<string name="battery_panel_title" msgid="7944156115535366613">"การใช้งานแบตเตอรี่"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ไม่สามารถใช้โหมดประหยัดแบตเตอรี่ระหว่างการชาร์จ"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"โหมดประหยัดแบตเตอรี่"</string>
diff --git a/packages/SystemUI/res/values-tl/config.xml b/packages/SystemUI/res/values-tl/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-tl/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 41326ab..ed6830e 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ang limitasyon"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Babala sa <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Work mode"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Night Light"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Naka-on ang Night Light, i-tap upang i-off"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Naka-off ang Night Light, i-tap upang i-on"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Walang mga kamakailang item"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Na-clear mo ang lahat"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Impormasyon ng Application"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Split Vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Split Custom"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Nasingil na"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Nagcha-charge"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> hanggang mapuno"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"Ang <xliff:g id="APP_NAME">%1$s</xliff:g> ang volume dialog"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"I-tap upang i-restore ang orihinal."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Ginagamit mo ang iyong profile sa trabaho"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Tawag"</item>
+ <item msgid="5997713001067658559">"System"</item>
+ <item msgid="7858983209929864160">"Ipa-ring"</item>
+ <item msgid="1850038478268896762">"Media"</item>
+ <item msgid="8265110906352372092">"Alarm"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. I-tap upang i-unmute."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. I-tap upang itakda na mag-vibrate. Maaaring i-mute ang mga serbisyo sa Accessibility."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. I-tap upang i-mute. Maaaring i-mute ang mga serbisyo sa Accessibility."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Higit pang mga setting"</string>
<string name="notification_done" msgid="5279426047273930175">"Tapos Na"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Mga kontrol sa notification ng <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Kulay at hitsura"</string>
- <string name="night_mode" msgid="3540405868248625488">"Night mode"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"I-calibrate ang display"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Naka-on"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Naka-off"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Awtomatikong i-on"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Lumipat sa Night Mode kapag naaangkop sa lokasyon at oras"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Kapag naka-on ang Night Mode"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Gumamit ng madilim na tema para sa Android OS"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Isaayos ang tint"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Isaayos ang liwanag"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Ilalapat ang madilim na tema sa mga mahalagang bahagi ng Android OS na karaniwang ipinapakita nang may maliwanag na tema, gaya ng Mga Setting."</string>
- <string name="color_apply" msgid="9212602012641034283">"Ilapat"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Kumpirmahin ang mga setting"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Maaaring hindi magamit ang device na ito dahil sa ilang setting ng kulay. I-click ang OK upang kumpirmahin ang mga setting ng kulay na ito, kung hindi ay mare-reset ang mga setting na ito pagkatapos ng 10 segundo."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Paggamit ng baterya"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Hindi available ang Pangtipid sa Baterya kapag nagcha-charge"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Pangtipid sa Baterya"</string>
diff --git a/packages/SystemUI/res/values-tr/config.xml b/packages/SystemUI/res/values-tr/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-tr/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index e95785f..8fae7975 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Dolaşımda"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Sınır: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> uyarısı"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Çalışma modu"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Gece Işığı"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Geçe Işığı açık, kapatmak için dokunun"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Gece Işığı kapalı, açmak için dokunun"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Yeni öğe yok"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Her şeyi sildiniz"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Uygulama Bilgileri"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Yatay Ayırma"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Dikey Ayırma"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Özel Ayırma"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Ödeme alındı"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Şarj oluyor"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Tam şarj olmasına <xliff:g id="CHARGING_TIME">%s</xliff:g> kaldı"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ses denetimi iletişim kutusu olarak ayarlandı"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Orijinali geri yüklemek için dokunun."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"İş profilinizi kullanıyorsunuz"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Çağrı"</item>
+ <item msgid="5997713001067658559">"Sistem"</item>
+ <item msgid="7858983209929864160">"Zili Çaldır"</item>
+ <item msgid="1850038478268896762">"Medya"</item>
+ <item msgid="8265110906352372092">"Alarm"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Sesi açmak için hafifçe dokunun."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Titreşime ayarlamak için hafifçe dokunun. Erişilebilirlik hizmetlerinin sesi kapatılabilir."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Sesi kapatmak için hafifçe dokunun. Erişilebilirlik hizmetlerinin sesi kapatılabilir."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Diğer ayarlar"</string>
<string name="notification_done" msgid="5279426047273930175">"Bitti"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> bildirim denetimleri"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Renk ve görünüm"</string>
- <string name="night_mode" msgid="3540405868248625488">"Gece modu"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Ekranı kalibre et"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Açık"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Kapalı"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Otomatik olarak aç"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Konuma ve günün saatine uygun şekilde Gece Modu\'na geç"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Gece Modu açık olduğunda"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android OS için koyu renk tema kullan"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Tonu ayarla"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Parlaklığı ayarla"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Koyu renk tema, Android OS\'nin normalde Ayarlar gibi açık renk bir temayla görüntülenen temel alanlarına uygulanır."</string>
- <string name="color_apply" msgid="9212602012641034283">"Uygula"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Ayarları onaylayın"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Bazı renkler bu cihazı kullanılmaz yapabilir. Bu renkleri onaylamak için Tamam\'ı tıklayın. Tıklamazsanız bu ayarlar 10 saniye sonra sıfırlanacaktır."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Pil kullanımı"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Şarj sırasında Pil Tasarrufu özelliği kullanılamaz"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Pil Tasarrufu"</string>
diff --git a/packages/SystemUI/res/values-uk/config.xml b/packages/SystemUI/res/values-uk/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-uk/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index f68521c..7eaf0db 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -36,14 +36,14 @@
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Сповіщення"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Низький рівень заряду акумулятора"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"Залишилося <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
- <string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"Залишилося <xliff:g id="PERCENTAGE">%s</xliff:g>. Режим заощадження заряду акумулятора ввімкнено."</string>
+ <string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"Залишилося <xliff:g id="PERCENTAGE">%s</xliff:g>. Увімкнено режим енергозбереження."</string>
<string name="invalid_charger" msgid="4549105996740522523">"Заряджання USB не підтримується.\nВикористовуйте лише наданий у комплекті зарядний пристрій."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"Заряджання через USB не підтримується."</string>
<string name="invalid_charger_text" msgid="5474997287953892710">"Використовуйте лише зарядний пристрій, який постачається в комплекті."</string>
<string name="battery_low_why" msgid="4553600287639198111">"Налаштування"</string>
- <string name="battery_saver_confirmation_title" msgid="5299585433050361634">"Увімкнути режим заощадження заряду акумулятора?"</string>
+ <string name="battery_saver_confirmation_title" msgid="5299585433050361634">"Увімкнути режим енергозбереження?"</string>
<string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"Увімкнути"</string>
- <string name="battery_saver_start_action" msgid="5576697451677486320">"Увімкнути режим заощадження заряду акумулятора"</string>
+ <string name="battery_saver_start_action" msgid="5576697451677486320">"Увімкнути режим енергозбереження"</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Налаштування"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
<string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"Повертати екран автоматично"</string>
@@ -145,7 +145,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Роумінг"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -323,6 +325,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Обмеження: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Застереження: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Робочий режим"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Нічний режим"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Нічний режим увімкнено. Торкніться, щоб вимкнути його"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Нічний режим вимкнено. Торкніться, щоб увімкнути його"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Немає нещодавніх завдань"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Ви очистили все"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Інформація про додаток"</string>
@@ -336,6 +341,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Розділити горизонтально"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Розділити вертикально"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Розділити (власний варіант)"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Заряджено"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Заряджається"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"До повного зарядження <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
@@ -392,9 +399,9 @@
<string name="user_remove_user_title" msgid="4681256956076895559">"Видалити користувача?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Усі додатки й дані цього користувача буде видалено."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Видалити"</string>
- <string name="battery_saver_notification_title" msgid="237918726750955859">"Режим заощадження заряду акумулятора ввімкнено"</string>
+ <string name="battery_saver_notification_title" msgid="237918726750955859">"Режим енергозбереження ввімкнено"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"Знижується продуктивність і обмежуються фонові дані"</string>
- <string name="battery_saver_notification_action_text" msgid="109158658238110382">"Вимкнути режим заощадження заряду акумулятора"</string>
+ <string name="battery_saver_notification_action_text" msgid="109158658238110382">"Вимкнути режим енергозбереження"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> отримає доступ до всіх даних, які відображаються на вашому екрані."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Більше не показувати"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Очистити все"</string>
@@ -440,6 +447,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> призначено регулятором гучності"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Торкніться, щоб відновити оригінал."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Ви в робочому профілі"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Виклик"</item>
+ <item msgid="5997713001067658559">"Система"</item>
+ <item msgid="7858983209929864160">"Дзвонити"</item>
+ <item msgid="1850038478268896762">"Медіа"</item>
+ <item msgid="8265110906352372092">"Будильник"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Торкніться, щоб увімкнути звук."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Торкніться, щоб налаштувати вібросигнал. Спеціальні можливості може бути вимкнено."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Торкніться, щоб вимкнути звук. Спеціальні можливості може бути вимкнено."</string>
@@ -510,24 +529,9 @@
<string name="notification_more_settings" msgid="816306283396553571">"Більше налаштувань"</string>
<string name="notification_done" msgid="5279426047273930175">"Готово"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Елементи керування сповіщеннями додатка <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Колір і вигляд"</string>
- <string name="night_mode" msgid="3540405868248625488">"Нічний режим"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Калібрувати дисплей"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Увімкнено"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Вимкнено"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Вмикати автоматично"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Переходити на нічний режим відповідно до місцезнаходження та часу доби"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Коли нічний режим увімкнено"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Використати нічну тему для ОС Android"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Налаштувати відтінок"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Регулювати яскравість"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Темна тема застосовується в основних областях ОС Android, які зазвичай відображаються у світлій темі, як-от у налаштуваннях."</string>
- <string name="color_apply" msgid="9212602012641034283">"Застосувати"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Підтвердити налаштування"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Деякі налаштування кольорів можуть зробити цей пристрій непридатним для використання. Натисніть OK, щоб підтвердити налаштування, інакше їх буде скинуто через 10 секунд."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Використання заряду"</string>
- <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Режим економії заряду акумулятора недоступний під час заряджання"</string>
- <string name="battery_detail_switch_title" msgid="6285872470260795421">"Режим економії заряду акумулятора"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Режим енергозбереження не можна ввімкнути під час заряджання"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Режим енергозбереження"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Знижується продуктивність і обмежується обмін даними у фоновому режимі"</string>
<string name="keyboard_key_button_template" msgid="6230056639734377300">"Кнопка <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
diff --git a/packages/SystemUI/res/values-ur-rPK/config.xml b/packages/SystemUI/res/values-ur-rPK/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-ur-rPK/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml
index 92139f1..5c0ed18 100644
--- a/packages/SystemUI/res/values-ur-rPK/strings.xml
+++ b/packages/SystemUI/res/values-ur-rPK/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"رومنگ"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> حد"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> وارننگ"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"کام موڈ"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"نائٹ لائٹ"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"نائٹ لائٹ آن ہے، آف کرنے کیلئے تھپتھپائیں"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"نائٹ لائٹ آف ہے، آن کرنے کیلئے تھپتھپائیں"</string>
<string name="recents_empty_message" msgid="808480104164008572">"کوئی حالیہ آئٹم نہیں"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"آپ نے سب کچھ صاف کر دیا ہے"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"ایپلیکیشن کی معلومات"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"بلحاظ افقی الگ کریں"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"بلحاظ عمودی الگ کریں"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"بلحاظ حسب ضرورت الگ کریں"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"چارج ہوگئی"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"چارج ہو رہی ہے"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> مکمل ہونے تک"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> والیوم ڈائلاگ ہے"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"اصل بحال کرنے کیلئے تھپتھپائیں۔"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"آپ اپنا دفتری پروفائل استعمال کر رہے ہیں۔"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"کال"</item>
+ <item msgid="5997713001067658559">"سسٹم"</item>
+ <item msgid="7858983209929864160">"رِنگ"</item>
+ <item msgid="1850038478268896762">"میڈیا"</item>
+ <item msgid="8265110906352372092">"الارم"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"بلوٹوتھ"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s۔ آواز چالو کرنے کیلئے تھپتھپائیں۔"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s۔ ارتعاش پر سیٹ کرنے کیلئے تھپتھپائیں۔ Accessibility سروسز شاید خاموش ہوں۔"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s۔ خاموش کرنے کیلئے تھپتھپائیں۔ Accessibility سروسز شاید خاموش ہوں۔"</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"مزید ترتیبات"</string>
<string name="notification_done" msgid="5279426047273930175">"ہوگیا"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے نوٹیفکیشن کنٹرولز"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"رنگ اور ظہور"</string>
- <string name="night_mode" msgid="3540405868248625488">"رات موڈ"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"نشان زد ڈسپلے"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"آن"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"آف"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"خودکار طور پر آن کریں"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"مقام اور دن کے وقت کی مناسبت سے نائٹ موڈ میں سوئچ کریں"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"جب نائٹ موڈ آن ہو"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android OS کیلئے ڈارک تھیم استعمال کریں"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"ٹنٹ ایڈجسٹ کریں"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"چمک کو ایڈجسٹ کریں"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"ڈارک تھیم Android OS کی بنیادی جگہوں پر لاگو کی جاتی ہے جو عام طور لائٹ تھیم میں ڈسپلے ہوتے ہیں، جیسے ترتیبات۔"</string>
- <string name="color_apply" msgid="9212602012641034283">"لاگو کریں"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"ترتیبات کی توثیق کریں"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"رنگوں کی کچھ ترتیبات اس آلے کو ناقابل استعمال بنا سکتی ہیں۔ رنگوں کی ان ترتیبات کی توثیق کرنے کیلئے ٹھیک ہے پر کلک کریں، بصورت دیگر 10 سیکنڈ بعد یہ ترتیبات ری سیٹ ہو جائیں گی۔"</string>
<string name="battery_panel_title" msgid="7944156115535366613">"بیٹری کا استعمال"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"چارجنگ کے دوران بیٹری سیور دستیاب نہیں ہے"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"بیٹری سیور"</string>
diff --git a/packages/SystemUI/res/values-uz-rUZ/config.xml b/packages/SystemUI/res/values-uz-rUZ/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-uz-rUZ/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index 84e2a29..ac6194f 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Rouming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -213,7 +215,7 @@
<string name="accessibility_quick_settings_location_on" msgid="5809937096590102036">"Joylashuv ma’lumotini yuborish yoqilgan."</string>
<string name="accessibility_quick_settings_location_changed_off" msgid="8526845571503387376">"Joylashuv ma’lumotini yuborish o‘chirildi."</string>
<string name="accessibility_quick_settings_location_changed_on" msgid="339403053079338468">"Joylashuv ma’lumotini yuborish yoqildi."</string>
- <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Uyg‘otkich signali <xliff:g id="TIME">%s</xliff:g> da chalinadi."</string>
+ <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Signal <xliff:g id="TIME">%s</xliff:g> da chalinadi."</string>
<string name="accessibility_quick_settings_close" msgid="3115847794692516306">"Panelni yopish."</string>
<string name="accessibility_quick_settings_more_time" msgid="3659274935356197708">"Ko‘proq vaqt."</string>
<string name="accessibility_quick_settings_less_time" msgid="2404728746293515623">"Kamroq vaqt."</string>
@@ -265,7 +267,7 @@
<string name="quick_settings_dnd_label" msgid="8735855737575028208">"Bezovta qilinmasin"</string>
<string name="quick_settings_dnd_priority_label" msgid="483232950670692036">"Faqat muhimlari"</string>
<string name="quick_settings_dnd_alarms_label" msgid="2559229444312445858">"Faqat signallar"</string>
- <string name="quick_settings_dnd_none_label" msgid="5025477807123029478">"Tinchlik saqlansin"</string>
+ <string name="quick_settings_dnd_none_label" msgid="5025477807123029478">"Jimjitlik"</string>
<string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"Bluetooth"</string>
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g>ta qurilma)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth o‘chirilgan"</string>
@@ -319,6 +321,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Cheklov: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Ogohlantirish: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Ish rejimi"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Tungi rejim"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Tunji rejim yoniq, o‘chirish uchun bosing"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Tunji rejim o‘chiq, yoqish uchun bosing"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Hozircha hech narsa yo‘q"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Hammasi o‘chirildi"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Ilova haqida ma’lumot"</string>
@@ -332,6 +337,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Gorizontal yo‘nalishda bo‘lish"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikal yo‘nalishda bo‘lish"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Boshqa usulda bo‘lish"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Batareya quvvati to‘ldi"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Quvvat olmoqda"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g>da to‘ladi"</string>
@@ -340,7 +347,7 @@
<string name="description_target_search" msgid="3091587249776033139">"Qidirish"</string>
<string name="description_direction_up" msgid="7169032478259485180">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> uchun yuqoriga suring."</string>
<string name="description_direction_left" msgid="7207478719805562165">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> uchun chapga suring."</string>
- <string name="zen_priority_introduction" msgid="3070506961866919502">"Turli ovoz va tebranishlar endi sizni bezovta qilmaydi. Biroq uyg‘otkich signallari, eslatmalar, tadbirlar haqidagi bildirishnomalar va siz tanlagan abonentlardan kelgan qo‘ng‘iroqlar bundan mustasno."</string>
+ <string name="zen_priority_introduction" msgid="3070506961866919502">"Turli ovoz va tebranishlar endi sizni bezovta qilmaydi. Biroq signallar, eslatmalar, tadbirlar haqidagi bildirishnomalar va siz tanlagan abonentlardan kelgan qo‘ng‘iroqlar bundan mustasno."</string>
<string name="zen_priority_customize_button" msgid="7948043278226955063">"Sozlash"</string>
<string name="zen_silence_introduction_voice" msgid="2284540992298200729">"Bu BARCHA, jumladan signallar, musiqa, videolar va o‘yinlardan keladigan tovush va tebranishlarni to‘sib qo‘yadi. Siz telefon qo‘ng‘iroqlarini bemalol amalga oshirishingiz mumkin."</string>
<string name="zen_silence_introduction" msgid="3137882381093271568">"Bu BARCHA, jumladan, signallar, musiqa, videolar va o‘yinlardan keladigan tovush va tebranishlarni to‘sib qo‘yadi."</string>
@@ -351,8 +358,8 @@
<string name="phone_hint" msgid="4872890986869209950">"Telefonni ochish uchun suring"</string>
<string name="voice_hint" msgid="8939888732119726665">"Ovozli yordam: belgidan boshlab suring"</string>
<string name="camera_hint" msgid="7939688436797157483">"Kamerani ochish uchun suring"</string>
- <string name="interruption_level_none_with_warning" msgid="5114872171614161084">"Tinchlik saqlansin. Ekrandan o‘qish dasturlari ham ishlamaydi."</string>
- <string name="interruption_level_none" msgid="6000083681244492992">"Tinchlik saqlansin"</string>
+ <string name="interruption_level_none_with_warning" msgid="5114872171614161084">"Jimjitlik – tinchlik saqlanadi. Ekrandan o‘qish dasturlari ham ishlamaydi."</string>
+ <string name="interruption_level_none" msgid="6000083681244492992">"Jimjitlik"</string>
<string name="interruption_level_priority" msgid="6426766465363855505">"Faqat muhimlari"</string>
<string name="interruption_level_alarms" msgid="5226306993448328896">"Faqat signallar"</string>
<string name="interruption_level_none_twoline" msgid="3957581548190765889">"Tinchlik\nsaqlansin"</string>
@@ -436,6 +443,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ovoz balandligini boshqaradi"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Aslini tiklash uchun bosing."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Siz ishchi profildan foydalanmoqdasiz"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Qo‘ng‘iroq"</item>
+ <item msgid="5997713001067658559">"Tizim"</item>
+ <item msgid="7858983209929864160">"Jiringlatish"</item>
+ <item msgid="1850038478268896762">"Multimedia"</item>
+ <item msgid="8265110906352372092">"Signal"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Ovozini yoqish uchun ustiga bosing."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tebranishni yoqish uchun ustiga bosing. Maxsus imkoniyatlar ishlamasligi mumkin."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Ovozini o‘chirish uchun ustiga bosing. Maxsus imkoniyatlar ishlamasligi mumkin."</string>
@@ -506,21 +525,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Boshqa sozlamalar"</string>
<string name="notification_done" msgid="5279426047273930175">"Tayyor"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> bildirishnomalarini boshqarish"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Rang va ko‘rinishi"</string>
- <string name="night_mode" msgid="3540405868248625488">"Tungi rejim"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Ekranni kalibrlash"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Yoniq"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"O‘chiq"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Avtomatik yoqish"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Joylashuv va vaqtga mos ravishda tungi rejimga o‘tish"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Agar tungi rejim yoniq bo‘lsa"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Android uchun to‘q rangli mavzudan foydalanish"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Rang tusini o‘zgartirish"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Yorqinlikni o‘zgartirish"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"To‘q rangli mavzu Android tizimining odatda och rangda ko‘rsatiladigan o‘zak sahifalariga (masalan, Sozlamalar) nisbatan qo‘llaniladi."</string>
- <string name="color_apply" msgid="9212602012641034283">"Qo‘llash"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Sozlamalarni tasdiqlang"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Ba’zi rang sozlamalari qurilmadan foydalanishni qiyinlashtirish mumkin. Tanlgan parametrlarni tasdiqlash uchun “OK” tugmasini bosing. Aks holda, ular 10 soniyadan so‘ng qayta tiklanadi."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Batareya sarfi"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Quvvat tejash rejimidan quvvatlash vaqtida foydalanib bo‘lmaydi"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Quvvat tejash rejimi"</string>
@@ -642,7 +646,7 @@
<string name="accessibility_quick_settings_settings" msgid="6132460890024942157">"Sozlamalarni ochish."</string>
<string name="accessibility_quick_settings_expand" msgid="2375165227880477530">"Tezkor sozlamalarni ochish."</string>
<string name="accessibility_quick_settings_collapse" msgid="1792625797142648105">"Tezkor sozlamalarni yopish."</string>
- <string name="accessibility_quick_settings_alarm_set" msgid="1863000242431528676">"Uyg‘otkich o‘rnatildi."</string>
+ <string name="accessibility_quick_settings_alarm_set" msgid="1863000242431528676">"Signal o‘rnatildi."</string>
<string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> sifatida kirgansiz"</string>
<string name="accessibility_quick_settings_no_internet" msgid="31890692343084075">"Internet yo‘q."</string>
<string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Tafsilotlarini ko‘rsatish."</string>
diff --git a/packages/SystemUI/res/values-vi/config.xml b/packages/SystemUI/res/values-vi/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-vi/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 24c2907..930eebe 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3,5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Chuyển vùng"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Cạnh"</string>
@@ -308,7 +310,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Đang dùng làm điểm truy cập Internet"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Điểm phát sóng"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Thông báo"</string>
- <string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Đèn nháy"</string>
+ <string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Đèn pin"</string>
<string name="quick_settings_cellular_detail_title" msgid="8575062783675171695">"Dữ liệu di động"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Sử dụng dữ liệu"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Dữ liệu còn lại"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Giới hạn <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Cảnh báo <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Chế độ làm việc"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Đèn đọc sách"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Đèn đọc sách được bật, nhấn để tắt"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Đèn đọc sách bị tắt, nhấn để bật"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Không có mục gần đây nào"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Bạn đã xóa mọi nội dung"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Thông tin ứng dụng"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Phân tách ngang"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Phân tách dọc"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Tùy chỉnh phân tách"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Đã sạc đầy"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Đang sạc"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> cho đến khi đầy"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> là hộp thoại khối lượng"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Nhấn để khôi phục ảnh chụp màn hình gốc."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Bạn đang sử dụng hồ sơ công việc của mình"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Gọi"</item>
+ <item msgid="5997713001067658559">"Hệ thống"</item>
+ <item msgid="7858983209929864160">"Chuông"</item>
+ <item msgid="1850038478268896762">"Phương tiện"</item>
+ <item msgid="8265110906352372092">"Báo thức"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Nhấn để bật tiếng."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Nhấn để đặt chế độ rung. Bạn có thể tắt tiếng dịch vụ trợ năng."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Nhấn để tắt tiếng. Bạn có thể tắt tiếng dịch vụ trợ năng."</string>
@@ -506,21 +525,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Cài đặt khác"</string>
<string name="notification_done" msgid="5279426047273930175">"Xong"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"Điều khiển thông báo <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Màu sắc và giao diện"</string>
- <string name="night_mode" msgid="3540405868248625488">"Chế độ ban đêm"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Hiệu chỉnh hiển thị"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Bật"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Tắt"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Tự động bật"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Chuyển sang Chế bộ ban đêm khi thích hợp cho vị trí và thời gian trong ngày"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Khi Chế độ ban đêm đang bật"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Sử dụng chủ đề sẫm màu cho Android OS"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Điều chỉnh phủ màu"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Điều chỉnh độ sáng"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Chủ đề sẫm màu được áp dụng cho các vùng chính của Android OS được hiển thị bình thường trong chủ đề sáng màu, chẳng hạn như Cài đặt."</string>
- <string name="color_apply" msgid="9212602012641034283">"Áp dụng"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Xác nhận cài đặt"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Một số cài đặt màu có thể khiến thiết bị này không sử dụng được. Hãy nhấp vào OK để xác nhận các cài đặt màu này, nếu không những cài đặt này sẽ được đặt lại sau 10 giây."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Mức sử dụng pin"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Trình tiết kiệm pin không khả dụng trong khi sạc"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Trình tiết kiệm pin"</string>
diff --git a/packages/SystemUI/res/values-w550dp-land/dimens.xml b/packages/SystemUI/res/values-w550dp-land/dimens.xml
index 4160c83..eaca9d7 100644
--- a/packages/SystemUI/res/values-w550dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-w550dp-land/dimens.xml
@@ -18,6 +18,4 @@
<resources>
<!-- Standard notification width + gravity -->
<dimen name="notification_panel_width">544dp</dimen>
-
- <dimen name="qs_expand_margin">32dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/config.xml b/packages/SystemUI/res/values-zh-rCN/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-zh-rCN/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 7613099..2548c4b 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"漫游中"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"EDGE"</string>
@@ -252,10 +254,10 @@
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"通知设置"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g>设置"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"屏幕会自动旋转。"</string>
- <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"屏幕锁定为横向模式。"</string>
+ <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"屏幕锁定为横屏模式。"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"屏幕锁定为纵向模式。"</string>
<string name="accessibility_rotation_lock_off_changed" msgid="8134601071026305153">"屏幕将会自动旋转。"</string>
- <string name="accessibility_rotation_lock_on_landscape_changed" msgid="3135965553707519743">"屏幕现已锁定为横向模式。"</string>
+ <string name="accessibility_rotation_lock_on_landscape_changed" msgid="3135965553707519743">"屏幕现已锁定为横屏模式。"</string>
<string name="accessibility_rotation_lock_on_portrait_changed" msgid="8922481981834012126">"屏幕现已锁定为纵向模式。"</string>
<string name="dessert_case" msgid="1295161776223959221">"甜品盒"</string>
<string name="start_dreams" msgid="5640361424498338327">"屏保"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"上限为<xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g>警告"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"工作模式"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"夜间模式"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"夜间模式已开启,点按即可关闭"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"夜间模式已关闭,点按即可开启"</string>
<string name="recents_empty_message" msgid="808480104164008572">"近期没有任何内容"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"您已清除所有内容"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"应用信息"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"水平分割"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"垂直分割"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"自定义分割"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"已充满"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"正在充电"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"还需<xliff:g id="CHARGING_TIME">%s</xliff:g>充满"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”已用作音量控制对话框"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"点按即可恢复原始设置。"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"您当前正在使用工作资料"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"通话"</item>
+ <item msgid="5997713001067658559">"系统"</item>
+ <item msgid="7858983209929864160">"铃声"</item>
+ <item msgid="1850038478268896762">"媒体"</item>
+ <item msgid="8265110906352372092">"闹钟"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"蓝牙"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s。点按即可取消静音。"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s。点按即可设为振动,但可能会同时将无障碍服务设为静音。"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s。点按即可设为静音,但可能会同时将无障碍服务设为静音。"</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"更多设置"</string>
<string name="notification_done" msgid="5279426047273930175">"完成"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g>通知设置"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"颜色和外观"</string>
- <string name="night_mode" msgid="3540405868248625488">"夜间模式"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"校准显示屏"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"开启"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"关闭"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"自动开启"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"根据地点和时间适时切换到夜间模式"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"夜间模式开启时"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"对 Android 操作系统使用深色主题背景"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"调整色调"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"调整亮度"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"系统会将深色主题背景应用于 Android 操作系统的核心区域(通常以浅色主题背景显示),例如“设置”部分。"</string>
- <string name="color_apply" msgid="9212602012641034283">"应用"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"确认设置"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"部分颜色设置可能会导致此设备无法使用。请点击“确定”确认这些颜色设置,否则,系统将在 10 秒后重置这些设置。"</string>
<string name="battery_panel_title" msgid="7944156115535366613">"电池使用情况"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"充电过程中无法使用省电模式"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"省电模式"</string>
@@ -542,7 +546,7 @@
<string name="keyboard_key_media_rewind" msgid="2654808213360820186">"快退"</string>
<string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"快进"</string>
<string name="keyboard_key_page_up" msgid="5654098530106845603">"向上翻页"</string>
- <string name="keyboard_key_page_down" msgid="8720502083731906136">"向下翻页"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"PgDn"</string>
<string name="keyboard_key_forward_del" msgid="1391451334716490176">"删除"</string>
<string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
<string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/config.xml b/packages/SystemUI/res/values-zh-rHK/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-zh-rHK/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 40d3429..c0172b1 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"漫遊"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -319,6 +321,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"上限為 <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> 警告"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"工作模式"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"夜燈模式"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"夜燈模式已開啟,輕按即可關閉"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"夜燈模式已關閉,輕按即可開啟"</string>
<string name="recents_empty_message" msgid="808480104164008572">"沒有最近項目"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"您已清除所有項目"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"應用程式資料"</string>
@@ -332,6 +337,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"水平分割"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"垂直分割"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"自訂分割"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"已完成充電"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"充電中"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g>後完成充電"</string>
@@ -436,6 +443,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」為音量對話框"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"輕按即可復原。"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"您正在使用工作設定檔"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"通話"</item>
+ <item msgid="5997713001067658559">"系統"</item>
+ <item msgid="7858983209929864160">"鈴聲"</item>
+ <item msgid="1850038478268896762">"媒體"</item>
+ <item msgid="8265110906352372092">"鬧鐘"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"藍牙"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s。輕按即可取消靜音。"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s。輕按即可設為震動。無障礙功能服務可能已經設為靜音。"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s。輕按即可設為靜音。無障礙功能服務可能已經設為靜音。"</string>
@@ -506,21 +525,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"更多設定"</string>
<string name="notification_done" msgid="5279426047273930175">"完成"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」通知控制項"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"顏色和外觀"</string>
- <string name="night_mode" msgid="3540405868248625488">"夜間模式"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"校準螢幕"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"已開啟"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"已關閉"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"自動開啟"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"在適當的位置和時間切換至「夜間模式」"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"「夜間模式」開啟時"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"在 Android OS 中使用深色主題背景"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"調整色調"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"調整亮度"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"系統會將深色主題背景套用至 Android OS 核心區域 (一般以淺色主題背景顯示),例如「設定」。"</string>
- <string name="color_apply" msgid="9212602012641034283">"套用"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"確認設定"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"部分顏色設定會令此裝置無法使用。請按一下 [確定] 加以確認,否則這些顏色設定將於 10 秒後重設。"</string>
<string name="battery_panel_title" msgid="7944156115535366613">"電池用量"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"充電時無法使用「省電模式」"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"省電模式"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/config.xml b/packages/SystemUI/res/values-zh-rTW/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-zh-rTW/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 7f03c5d..66c1264 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"漫遊中"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"上限為 <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> 警告"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"工作模式"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"夜燈"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"夜燈功能已開啟,輕觸即可關閉"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"夜燈功能已關閉,輕觸即可開啟"</string>
<string name="recents_empty_message" msgid="808480104164008572">"最近沒有任何項目"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"您已清除所有工作"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"應用程式資訊"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"水平分割"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"垂直分割"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"自訂分割"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"已充飽"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"充電中"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g>後充飽"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」現在是預設的音量控制對話方塊。"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"輕觸即可恢復原始設定。"</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"您正在使用 Work 設定檔"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"通話"</item>
+ <item msgid="5997713001067658559">"系統"</item>
+ <item msgid="7858983209929864160">"鈴聲"</item>
+ <item msgid="1850038478268896762">"媒體"</item>
+ <item msgid="8265110906352372092">"鬧鐘"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"藍牙"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s。輕觸即可取消靜音。"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s。輕觸即可設為震動,但系統可能會將無障礙服務一併設為靜音。"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s。輕觸即可設為靜音,但系統可能會將無障礙服務一併設為靜音。"</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"更多設定"</string>
<string name="notification_done" msgid="5279426047273930175">"完成"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」通知控制項"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"顏色和外觀"</string>
- <string name="night_mode" msgid="3540405868248625488">"夜間模式"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"校正顯示畫面"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"開啟"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"關閉"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"自動開啟"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"根據地點和時段適時切換到「夜間模式」"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"「夜間模式」開啟時"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"針對 Android 作業系統使用深色主題"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"調整色調"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"調整亮度"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"深色主題會套用到 Android 作業系統的核心區塊 (一般是以淺色主題顯示),例如「設定」區塊。"</string>
- <string name="color_apply" msgid="9212602012641034283">"套用"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"確認設定"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"部分顏色設定可能會造成這部裝置無法使用。請按一下 [確定] 來確認您要使用這類顏色設定,否則系統將在 10 秒後重設這些設定。"</string>
<string name="battery_panel_title" msgid="7944156115535366613">"電池用量"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"充電時無法使用節約耗電量模式"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"節約耗電量"</string>
diff --git a/packages/SystemUI/res/values-zu/config.xml b/packages/SystemUI/res/values-zu/config.xml
new file mode 100644
index 0000000..5309563
--- /dev/null
+++ b/packages/SystemUI/res/values-zu/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
+</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index bedaeca..49bf0bd 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -143,7 +143,9 @@
<string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
<string name="accessibility_data_connection_lte" msgid="5413468808637540658">"I-LTE"</string>
+ <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"I-LTE+"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Iyazulazula"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Ekucupheleni"</string>
@@ -317,6 +319,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> umkhawulo"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> isexwayiso"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Imodi yomsebenzi"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Ukukhanya kwasebusuku"</string>
+ <string name="quick_settings_night_display_summary_on" msgid="1814901757887526769">"Ukukhanya kwasebusuku kuvuliwe, thepha ukuze uvale"</string>
+ <string name="quick_settings_night_display_summary_off" msgid="7892102914128777905">"Ukukhanya kwasebusuku kuvaliwe, thepha ukuze uvule"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Azikho izinto zakamuva"</string>
<string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Usule yonke into"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Ulwazi lohlelo lokusebenza"</string>
@@ -330,6 +335,8 @@
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Hlukanisa okuvundlile"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Hlukanisa okumile"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Hlukanisa kwezifiso"</string>
+ <string-array name="recents_blacklist_array">
+ </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Kushajiwe"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Iyashaja"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> ize igcwale"</string>
@@ -434,6 +441,18 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> yingxoxo yevolumu"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Thepha ukuze ubuyisele okwasekuqaleni."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Usebenzisa iphrofayela yakho yomsebenzi"</string>
+ <string-array name="volume_stream_titles">
+ <item msgid="5841843895402729630">"Shayela"</item>
+ <item msgid="5997713001067658559">"Isistimu"</item>
+ <item msgid="7858983209929864160">"Khalisa"</item>
+ <item msgid="1850038478268896762">"Abezindaba"</item>
+ <item msgid="8265110906352372092">"I-Alamu"</item>
+ <item msgid="5339394737636839168"></item>
+ <item msgid="2951313578278086204">"I-Bluetooth"</item>
+ <item msgid="2919807739709798970"></item>
+ <item msgid="150349973435223405"></item>
+ <item msgid="6761963760295549099"></item>
+ </string-array>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Thepha ukuze ususe ukuthula."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Thepha ukuze usethe ukudlidliza. Amasevisi okufinyelela angathuliswa."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Thepha ukuze uthulise. Amasevisi okufinyelela angathuliswa."</string>
@@ -504,21 +523,6 @@
<string name="notification_more_settings" msgid="816306283396553571">"Izilungiselelo eziningi"</string>
<string name="notification_done" msgid="5279426047273930175">"Kwenziwe"</string>
<string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> izilawuli zasaziso"</string>
- <string name="color_and_appearance" msgid="1254323855964993144">"Umbala nokubonakala"</string>
- <string name="night_mode" msgid="3540405868248625488">"Imodi yasebusuku"</string>
- <string name="calibrate_display" msgid="5974642573432039217">"Sika isibonisi"</string>
- <string name="night_mode_on" msgid="5597545513026541108">"Vuliwe"</string>
- <string name="night_mode_off" msgid="8035605276956057508">"Valiwe"</string>
- <string name="turn_on_automatically" msgid="4167565356762016083">"Vula ngokuzenzakalela"</string>
- <string name="turn_on_auto_summary" msgid="2190994512406701520">"Shintshela kwimodi yasebusuku njengokuqondile ngendawo nesikhathi sosuku"</string>
- <string name="when_night_mode_on" msgid="2969436026899172821">"Uma imodi yasebusuku ivulekile"</string>
- <string name="use_dark_theme" msgid="2900938704964299312">"Sebenzisa ingqikithi emnyama ku-Android OS"</string>
- <string name="adjust_tint" msgid="3398569573231409878">"Lungisa i-tint"</string>
- <string name="adjust_brightness" msgid="980039329808178246">"Lungisa ukukhanya"</string>
- <string name="night_mode_disclaimer" msgid="598914896926759578">"Itimu emnyama isetshenziswa ezindaweni eziqinile ze-Android OS ezivamise ukuoniswa ngetimu ekhanyayo, efana nezilungiselelo."</string>
- <string name="color_apply" msgid="9212602012641034283">"Sebenzisa"</string>
- <string name="color_revert_title" msgid="4746666545480534663">"Qinisekisa izilungiselelo"</string>
- <string name="color_revert_message" msgid="9116001069397996691">"Ezinye izilungiselelo zombala zingenza le divayisi ingasebenziseki. Chofoza ku-KULUNGILE ukuze uqinisekise lezi zilungiselelo zombala, uma kungenjalo lezi zilungiselelo zizosethwa kabusha ngemuva kwamasekhondi angu-10."</string>
<string name="battery_panel_title" msgid="7944156115535366613">"Ukusetshenziswa kwebhethri"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Isilondolozi sebhethri asitholakali ngesikhathi sokushaja"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Isilondolozi sebhethri"</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index cb51649..52565ba 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -30,18 +30,13 @@
<color name="batterymeter_charge_color">#FFFFFFFF</color>
<color name="batterymeter_bolt_color">#FFFFFFFF</color>
<color name="qs_batterymeter_frame_color">#FF404040</color>
- <color name="system_primary_color">#ff263238</color><!-- blue grey 900 -->
- <color name="system_secondary_color">#ff37474F</color><!-- blue grey 800 -->
- <color name="system_accent_color">#ff80CBC4</color><!-- deep teal 200 -->
- <color name="system_warning_color">#fff4511e</color><!-- deep orange 600 -->
+ <color name="system_warning_color">@*android:color/system_error</color>
<color name="qs_text">#FFFFFFFF</color>
<color name="qs_tile_divider">#29ffffff</color><!-- 16% white -->
<color name="qs_subhead">#99FFFFFF</color><!-- 60% white -->
- <color name="qs_detail_empty">#24B0BEC5</color><!-- 14% blue grey 200 -->
- <color name="qs_detail_button">#FFB0BEC5</color><!-- 100% blue grey 200 -->
+ <color name="qs_detail_button">@*android:color/quaternary_device_default_settings</color>
<color name="qs_detail_button_white">#B3FFFFFF</color><!-- 70% white -->
<color name="qs_detail_transition">#66FFFFFF</color>
- <color name="qs_detail_progress_track">#99009688</color><!-- 60% deep teal 500 -->
<color name="data_usage_secondary">#99FFFFFF</color><!-- 60% white -->
<color name="data_usage_graph_track">#33FFFFFF</color><!-- 20% white -->
<color name="data_usage_graph_warning">#FFFFFFFF</color>
@@ -99,15 +94,11 @@
<!-- The color of the ripples on the tinted notifications -->
<color name="notification_ripple_tinted_color">#30ffffff</color>
- <!-- The color of the circle around the primary user in the user switcher -->
- <color name="current_user_border_color">@color/system_accent_color</color>
-
<!-- The color of the gear shown behind a notification -->
<color name="notification_gear_color">#ff757575</color>
<!-- The "inside" of a notification, reached via longpress -->
<color name="notification_guts_bg_color">#eeeeee</color>
- <color name="notification_guts_slider_color">@*android:color/material_deep_teal_500</color>
<color name="notification_guts_disabled_slider_color">@*android:color/material_grey_300</color>
<color name="notification_guts_secondary_slider_color">#858383</color>
<color name="notification_guts_icon_tint">#8a000000</color>
@@ -126,13 +117,11 @@
<!-- Shadow color for the furthest pixels around the fake shadow for recents. -->
<color name="fake_shadow_end_color">#03000000</color>
- <color name="screen_pinning_nav_icon_highlight_outer">#4080cbc4</color><!-- 25% deep teal 200 -->
- <color name="screen_pinning_request_bg">#ff009688</color><!-- deep teal 500 -->
<color name="screen_pinning_request_window_bg">#80000000</color>
<color name="segmented_buttons_background">#14FFFFFF</color><!-- 8% white -->
<color name="segmented_button_selected">#FFFFFFFF</color>
- <color name="segmented_button_unselected">#FFB0BEC5</color><!-- blue grey 200 -->
+ <color name="segmented_button_unselected">@*android:color/quaternary_device_default_settings</color>
<color name="dark_mode_icon_color_single_tone">#99000000</color>
<color name="dark_mode_icon_color_dual_tone_background">#3d000000</color>
@@ -142,13 +131,9 @@
<color name="light_mode_icon_color_dual_tone_background">#4dffffff</color>
<color name="light_mode_icon_color_dual_tone_fill">#ffffff</color>
- <color name="zen_introduction_message_background">#ff009688</color><!-- deep teal 500 -->
<color name="volume_icon_color">#ffffffff</color>
<color name="volume_settings_icon_color">#7fffffff</color>
- <color name="volume_slider_inactive">#FFB0BEC5</color><!-- blue grey 200 -->
-
- <color name="fab_ripple">#1fffffff</color><!-- 12% white -->
- <color name="fab_shape">#ff009688</color><!-- Teal 500 -->
+ <color name="volume_slider_inactive">@*android:color/quaternary_device_default_settings</color>
<color name="docked_divider_background">#ff000000</color>
<color name="docked_divider_handle">#ffffff</color>
@@ -166,16 +151,10 @@
<color name="qs_tile_tint_inactive">#4dffffff</color>
<color name="qs_tile_tint_active">#ffffffff</color>
- <color name="switch_bar_background">#ff37474f</color>
- <color name="switch_accent_color">#ff7fcac3</color>
-
<!-- Keyboard shortcuts colors -->
- <color name="ksh_system_group_color">@color/material_deep_teal_500</color>
<color name="ksh_application_group_color">#fff44336</color>
<color name="ksh_keyword_color">#d9000000</color>
<color name="ksh_key_item_color">@color/material_grey_600</color>
<color name="ksh_key_item_background">@color/material_grey_100</color>
- <!-- Background color of edit overflow -->
- <color name="qs_edit_overflow_bg">#455A64</color>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 02b1b50..8d44048 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -103,9 +103,19 @@
wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location
</string>
+ <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
+ <string name="quick_settings_tiles_stock" translatable="false">
+ wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,work,cast,night
+ </string>
+
<!-- The tiles to display in QuickSettings -->
<string name="quick_settings_tiles" translatable="false">default</string>
+ <!-- The tiles to display in QuickSettings in retail mode -->
+ <string name="quick_settings_tiles_retail_mode" translatable="false">
+ cell,battery,dnd,flashlight,rotation,location
+ </string>
+
<!-- Whether or not the RSSI tile is capitalized or not. -->
<bool name="quick_settings_rssi_tile_capitalization">true</bool>
@@ -116,6 +126,9 @@
<!-- Should "4G" be shown instead of "LTE" when the network is NETWORK_TYPE_LTE? -->
<bool name="config_show4GForLTE">true</bool>
+ <!-- Should "LTE"/"4G" be shown instead of "LTE+"/"4G+" when on NETWORK_TYPE_LTE_CA? -->
+ <bool name="config_hideLtePlus">false</bool>
+
<!-- milliseconds before the heads up notification auto-dismisses. -->
<integer name="heads_up_notification_decay">5000</integer>
@@ -205,26 +218,36 @@
<!-- Doze: should notifications be used as a pulse signal? -->
<bool name="doze_pulse_on_notifications">true</bool>
- <!-- Doze: when to pulse after a buzzworthy notification arrives -->
- <string name="doze_pulse_schedule" translatable="false">10s,30s,60s</string>
-
- <!-- Doze: maximum number of times the notification pulse schedule can be reset -->
- <integer name="doze_pulse_schedule_resets">2</integer>
-
<!-- Doze: duration to avoid false pickup gestures triggered by notification vibrations -->
<integer name="doze_pickup_vibration_threshold">2000</integer>
- <!-- Doze: can we assume the pickup sensor includes a proximity check? -->
+ <!-- Doze: can we assume the pickup sensor includes a proximity check?
+ This is ignored if doze_pickup_subtype_performs_proximity_check is not empty.
+ @deprecated: use doze_pickup_subtype_performs_proximity_check instead.-->
<bool name="doze_pickup_performs_proximity_check">false</bool>
+ <!-- Doze: a list of pickup sensor subtypes that perform a proximity check before they trigger.
+ If not empty, either * or !* must appear to specify the default.
+ If empty, falls back to doze_pickup_performs_proximity_check.
+
+ Examples: 1,2,3,!* -> subtypes 1,2 and 3 perform the check, all others don't.
+ !1,!2,* -> subtypes 1 and 2 don't perform the check, all others do.
+ !8,* -> subtype 8 does not perform the check, all others do
+ 1,1,* -> illegal, every item may only appear once
+ 1,!1,* -> illegal, no contradictions allowed
+ 1,2 -> illegal, need either * or !*
+ 1,,4a3 -> illegal, no empty or non-numeric terms allowed
+ -->
+ <string name="doze_pickup_subtype_performs_proximity_check"></string>
+
<!-- Doze: pulse parameter - how long does it take to fade in? -->
<integer name="doze_pulse_duration_in">900</integer>
<!-- Doze: pulse parameter - how long does it take to fade in after a pickup? -->
- <integer name="doze_pulse_duration_in_pickup">300</integer>
+ <integer name="doze_pulse_duration_in_pickup">130</integer>
<!-- Doze: pulse parameter - once faded in, how long does it stay visible? -->
- <integer name="doze_pulse_duration_visible">3000</integer>
+ <integer name="doze_pulse_duration_visible">6000</integer>
<!-- Doze: pulse parameter - how long does it take to fade out? -->
<integer name="doze_pulse_duration_out">600</integer>
@@ -265,4 +288,3 @@
<bool name="quick_settings_show_full_alarm">false</bool>
</resources>
-
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2a4752a..fe67808 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -57,6 +57,9 @@
<!-- The amount to scale each of the status bar icons by. A value of 1 means no scaling. -->
<item name="status_bar_icon_scale_factor" format="float" type="dimen">1.0</item>
+ <!-- max height of a notification such that the content can still fade out when closing -->
+ <dimen name="max_notification_fadeout_height">100dp</dimen>
+
<!-- Height of a small notification in the status bar-->
<dimen name="notification_min_height">92dp</dimen>
@@ -130,7 +133,7 @@
<dimen name="close_handle_underlap">32dp</dimen>
<!-- Height of the status bar header bar -->
- <dimen name="status_bar_header_height">80dp</dimen>
+ <dimen name="status_bar_header_height">104dp</dimen>
<!-- Height of the status bar header bar when expanded -->
<dimen name="status_bar_header_height_expanded">116dp</dimen>
@@ -141,6 +144,9 @@
<!-- Margin start of the system icons super container -->
<dimen name="system_icons_super_container_margin_start">16dp</dimen>
+ <!-- Margin end of the system icons super container when the avatar is missing. -->
+ <dimen name="system_icons_super_container_avatarless_margin_end">6dp</dimen>
+
<!-- Width for the notification panel and related windows -->
<dimen name="match_parent">-1px</dimen>
<dimen name="standard_notification_panel_width">416dp</dimen>
@@ -172,10 +178,6 @@
<dimen name="qs_tile_margin_top">16dp</dimen>
<dimen name="qs_quick_tile_size">48dp</dimen>
<dimen name="qs_quick_tile_padding">12dp</dimen>
- <dimen name="qs_date_anim_translation">32dp</dimen>
- <dimen name="qs_date_alarm_anim_translation">22dp</dimen>
- <dimen name="qs_date_collapsed_text_size">14sp</dimen>
- <dimen name="qs_date_text_size">16sp</dimen>
<dimen name="qs_header_gear_translation">16dp</dimen>
<dimen name="qs_page_indicator_width">16dp</dimen>
<dimen name="qs_page_indicator_height">8dp</dimen>
@@ -202,7 +204,6 @@
<dimen name="qs_detail_margin_top">28dp</dimen>
<dimen name="qs_data_usage_text_size">14sp</dimen>
<dimen name="qs_data_usage_usage_text_size">36sp</dimen>
- <dimen name="qs_expand_margin">0dp</dimen>
<dimen name="qs_battery_padding">2dp</dimen>
<dimen name="qs_detail_items_padding_top">4dp</dimen>
@@ -377,6 +378,8 @@
<!-- The font size of the date in QS -->
<dimen name="qs_date_collapsed_size">14sp</dimen>
+ <!-- Amount the date/time move when emergency calls only is present -->
+ <dimen name="qs_date_time_translation">8dp</dimen>
<!-- Battery level text padding end when in expanded QS and on Keyguard -->
<dimen name="battery_level_padding_end">2dp</dimen>
@@ -538,6 +541,19 @@
<!-- Volume dialog root view bottom margin, at rest -->
<dimen name="volume_dialog_margin_bottom">4dp</dimen>
+ <dimen name="volume_dialog_collapsed_padding_top">8dp</dimen>
+ <dimen name="volume_dialog_expanded_spacer">14dp</dimen>
+ <dimen name="volume_dialog_padding_end">40dp</dimen>
+
+ <dimen name="volume_row_padding_bottom">9.4dp</dimen>
+ <dimen name="volume_row_padding_start">4dp</dimen>
+ <dimen name="volume_row_header_padding_start">16dp</dimen>
+ <dimen name="volume_row_height">64dp</dimen>
+ <dimen name="volume_row_slider_height">48dp</dimen>
+ <dimen name="volume_row_slider_padding_start">12dp</dimen>
+
+ <dimen name="volume_expander_margin_end">2dp</dimen>
+ <dimen name="volume_expander_margin_top">6dp</dimen>
<!-- Padding between icon and text for managed profile toast -->
<dimen name="managed_profile_toast_padding">4dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f7a169c..6c48b25 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -348,9 +348,15 @@
<!-- Content description of the data connection type 4G for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_data_connection_4g">4G</string>
+ <!-- Content description of the data connection type 4G for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_data_connection_4g_plus">4G+</string>
+
<!-- Content description of the data connection type LTE for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_data_connection_lte">LTE</string>
+ <!-- Content description of the data connection type LTE+ for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_data_connection_lte_plus">LTE+</string>
+
<!-- Content description of the data connection type CDMA for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_data_connection_cdma">CDMA</string>
@@ -749,6 +755,12 @@
<string name="quick_settings_cellular_detail_data_warning"><xliff:g id="data_limit" example="2.0 GB">%s</xliff:g> warning</string>
<!-- QuickSettings: Work mode [CHAR LIMIT=NONE] -->
<string name="quick_settings_work_mode_label">Work mode</string>
+ <!-- QuickSettings: Label for the toggle to activate Night display (renamed "Night Light" with title caps). [CHAR LIMIT=20] -->
+ <string name="quick_settings_night_display_label">Night Light</string>
+ <!-- QuickSettings: Summary for the toggle to deactivate Night display when it's on (renamed "Night Light" with title caps). [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_night_display_summary_on">Night Light on, tap to turn off</string>
+ <!-- QuickSettings: Label for the toggle to activate Night display when it's off (renamed "Night Light" with title caps). [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_night_display_summary_off">Night Light off, tap to turn on</string>
<!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
<string name="recents_empty_message">No recent items</string>
@@ -778,6 +790,10 @@
<!-- Recents: MultiStack add stack split custom radio button. [CHAR LIMIT=NONE] -->
<string name="recents_multistack_add_stack_dialog_split_custom">Split Custom</string>
+ <!-- Fully qualified activity class names to be blacklisted in Recents, add package names into overlay as needed -->
+ <string-array name="recents_blacklist_array">
+ </string-array>
+
<!-- Expanded Status Bar Header: Battery Charged [CHAR LIMIT=40] -->
<string name="expanded_header_battery_charged">Charged</string>
@@ -1093,14 +1109,14 @@
<!-- Toast shown when user unlocks screen and managed profile activity is in the foreground -->
<string name="managed_profile_foreground_toast">You\'re using your work profile</string>
- <string-array name="volume_stream_titles" translatable="false">
- <item>Voice calls</item> <!-- STREAM_VOICE_CALL -->
+ <string-array name="volume_stream_titles">
+ <item>Call</item> <!-- STREAM_VOICE_CALL -->
<item>System</item> <!-- STREAM_SYSTEM -->
- <item>Notifications</item> <!-- STREAM_RING -->
+ <item>Ring</item> <!-- STREAM_RING -->
<item>Media</item> <!-- STREAM_MUSIC -->
- <item>Alarms</item> <!-- STREAM_ALARM -->
+ <item>Alarm</item> <!-- STREAM_ALARM -->
<item></item> <!-- STREAM_NOTIFICATION -->
- <item>Bluetooth calls</item> <!-- STREAM_BLUETOOTH_SCO -->
+ <item>Bluetooth</item> <!-- STREAM_BLUETOOTH_SCO -->
<item></item> <!-- STREAM_SYSTEM_ENFORCED -->
<item></item> <!-- STREAM_DTMF -->
<item></item> <!-- STREAM_TTS -->
@@ -1333,59 +1349,6 @@
<!-- Notification: Gear: Content description for the gear. [CHAR LIMIT=NONE] -->
<string name="notification_gear_accessibility"><xliff:g id="app_name" example="YouTube">%1$s</xliff:g> notification controls</string>
- <!-- SysUI Tuner: Color and appearance screen title [CHAR LIMIT=50] -->
- <string name="color_and_appearance">Color and appearance</string>
-
- <!-- SysUI Tuner: Name of the night mode feature [CHAR LIMIT=30] -->
- <string name="night_mode">Night mode</string>
-
- <!-- SysUI Tuner: Name of calibrate display dialog [CHAR LIMIT=30] -->
- <string name="calibrate_display">Calibrate display</string>
-
- <!-- SysUI Tuner: Summary of night mode when its on [CHAR LIMIT=NONE] -->
- <string name="night_mode_on">On</string>
-
- <!-- SysUI Tuner: Summary of night mode when its off [CHAR LIMIT=NONE] -->
- <string name="night_mode_off">Off</string>
-
- <!-- SysUI Tuner: Label for switch to turn on night mode automatically [CHAR LIMIT=50] -->
- <string name="turn_on_automatically">Turn on automatically</string>
-
- <!-- SysUI Tuner: Summary for switch to turn on night mode automatically [CHAR LIMIT=NONE] -->
- <string name="turn_on_auto_summary">Switch into Night Mode as appropriate for location and time of day</string>
-
- <!-- SysUI Tuner: Label for section controlling what night mode does [CHAR LIMIT=60] -->
- <string name="when_night_mode_on">When Night Mode is on</string>
-
- <!-- SysUI Tuner: Switch controlling whether dark theme is turned on with night mode [CHAR LIMIT=45] -->
- <string name="use_dark_theme">Use dark theme for Android OS</string>
-
- <!-- SysUI Tuner: Switch controlling whether tint is changed with night mode [CHAR LIMIT=45] -->
- <string name="adjust_tint">Adjust tint</string>
-
- <!-- SysUI Tuner: Switch controlling whether brightness is changed with night mode [CHAR LIMIT=45] -->
- <string name="adjust_brightness">Adjust brightness</string>
-
- <!-- SysUI Tuner: Disclaimer about using dark theme with night mode [CHAR LIMIT=NONE] -->
- <string name="night_mode_disclaimer">The dark theme is applied to
- core areas of Android OS that are normally displayed in a light theme,
- such as Settings.</string>
-
- <!-- Button to apply settings [CHAR LIMIT=30] -->
- <string name="color_apply">Apply</string>
-
- <!-- Title of warning dialog about bad color settings. [CHAR LIMIT=30] -->
- <string name="color_revert_title">Confirm settings</string>
-
- <!-- Message warning user about custom color settings [CHAR LIMIT=NONE] -->
- <string name="color_revert_message">Some color settings can make this
- device unusable. Click OK to confirm these color settings,
- otherwise these settings will reset after 10 seconds.</string>
-
- <string name="color_modification_r" translatable="false">R</string>
- <string name="color_modification_g" translatable="false">G</string>
- <string name="color_modification_b" translatable="false">B</string>
-
<!-- Title of the battery settings detail panel [CHAR LIMIT=20] -->
<string name="battery_panel_title">Battery usage</string>
@@ -1666,6 +1629,9 @@
<!-- Accessibility label for the notification icons in the collapsed status bar. Not shown on screen [CHAR LIMIT=NONE] -->
<string name="accessibility_desc_notification_icon"><xliff:g name="app_name" example="Gmail">%1$s</xliff:g> notification: <xliff:g name="notification_text" example="5 new messages">%2$s</xliff:g></string>
+ <!-- Label for button that reports a touch that was wrongly rejected by the lockscreen. For debugging only. [CHAR LIMIT=NONE] -->
+ <string name="report_rejected_touch" translatable="false">Report rejected touch</string>
+
<!-- Multi-Window strings -->
<!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity and that things might crash/not work properly [CHAR LIMIT=NONE] -->
<string name="dock_forced_resizable">App may not work with split-screen.</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index aeb484f..1ee13e9 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -132,7 +132,7 @@
<style name="TextAppearance.QS.DetailItemSecondary">
<item name="android:textSize">@dimen/qs_detail_item_secondary_text_size</item>
- <item name="android:textColor">@color/system_accent_color</item>
+ <item name="android:textColor">?android:attr/colorAccent</item>
</style>
<style name="TextAppearance.QS.Introduction">
@@ -177,7 +177,7 @@
<style name="TextAppearance.QS.DataUsage.Usage">
<item name="android:textSize">@dimen/qs_data_usage_usage_text_size</item>
- <item name="android:textColor">@color/system_accent_color</item>
+ <item name="android:textColor">?android:attr/colorAccent</item>
</style>
<style name="TextAppearance.QS.DataUsage.Secondary">
@@ -217,19 +217,13 @@
<style name="Animation.StatusBar">
</style>
- <style name="systemui_theme" parent="@android:style/Theme.DeviceDefault">
- <item name="android:colorPrimary">@color/system_primary_color</item>
- <item name="android:colorControlActivated">@color/system_accent_color</item>
- </style>
+ <style name="systemui_theme" parent="@android:style/Theme.DeviceDefault" />
<style name="systemui_theme_remote_input" parent="@android:style/Theme.DeviceDefault.Light">
<item name="android:colorAccent">@color/remote_input_accent</item>
</style>
- <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog">
- <item name="android:colorPrimary">@color/system_primary_color</item>
- <item name="android:colorControlActivated">@color/system_accent_color</item>
- </style>
+ <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog" />
<style name="Theme.SystemUI.Dialog.Alert" parent="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
@@ -296,7 +290,7 @@
<style name="TextAppearance.Volume.ZenDetail">
<item name="android:textSize">14sp</item>
<item name="android:fontFamily">sans-serif</item>
- <item name="android:textColor">#ffb0b3c5</item>
+ <item name="android:textColor">@*android:color/quaternary_device_default_settings</item>
</style>
<style name="VolumeButtons" parent="@android:style/Widget.Material.Button.Borderless">
@@ -320,12 +314,12 @@
<item name="android:layout_height">48dp</item>
</style>
- <style name="TunerSettings" parent="@android:style/Theme.Material.Settings">
+ <style name="TunerSettings" parent="@android:style/Theme.DeviceDefault.Settings">
<item name="android:windowActionBar">false</item>
<item name="preferenceTheme">@style/TunerPreferenceTheme</item>
</style>
- <style name="TunerPreferenceTheme" parent="@android:style/Theme.Material.Settings">
+ <style name="TunerPreferenceTheme" parent="@android:style/Theme.DeviceDefault.Settings">
<item name="dropdownPreferenceStyle">@style/Preference.DropDown.Material</item>
</style>
@@ -341,16 +335,16 @@
</style>
<style name="TextAppearance.NotificationGuts.Secondary">
- <item name="android:alpha">.54</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
<style name="TextAppearance.NotificationGuts.Primary">
- <item name="android:alpha">.87</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textSize">16sp</item>
</style>
<style name="TextAppearance.NotificationGuts.Radio">
- <item name="android:alpha">.87</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="TextAppearance.NotificationGuts.Button">
@@ -358,15 +352,11 @@
<item name="android:textAllCaps">true</item>
<item name="android:fontFamily">sans-serif-medium</item>
<item name="android:gravity">center</item>
- <item name="android:textColor">@*android:color/material_deep_teal_500</item>
+ <item name="android:textColor">?android:attr/colorAccent</item>
</style>
- <style name="ThemeOverlay.SwitchBar" parent="@android:style/ThemeOverlay">
- <item name="android:colorAccent">@color/switch_accent_color</item>
- </style>
-
- <style name="edit_theme" parent="@android:style/Theme.Material">
- <item name="android:colorBackground">@color/qs_edit_overflow_bg</item>
+ <style name="edit_theme" parent="@*android:style/Theme.DeviceDefault.Settings.Dark">
+ <item name="android:colorBackground">?android:attr/colorSecondary</item>
</style>
</resources>
diff --git a/packages/SystemUI/res/xml/color_and_appearance.xml b/packages/SystemUI/res/xml/color_and_appearance.xml
deleted file mode 100644
index 21f890e..0000000
--- a/packages/SystemUI/res/xml/color_and_appearance.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:sysui="http://schemas.android.com/apk/res-auto"
- android:title="@string/color_and_appearance">
-
- <Preference
- android:key="night_mode"
- android:title="@string/night_mode"
- android:fragment="com.android.systemui.tuner.NightModeFragment" />
-
- <com.android.systemui.tuner.CalibratePreference
- android:key="calibrate"
- android:title="@string/calibrate_display" />
-
-</PreferenceScreen>
diff --git a/packages/SystemUI/res/xml/night_mode.xml b/packages/SystemUI/res/xml/night_mode.xml
deleted file mode 100644
index 34af820..0000000
--- a/packages/SystemUI/res/xml/night_mode.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:sysui="http://schemas.android.com/apk/res-auto"
- android:title="@string/night_mode">
-
- <SwitchPreference
- android:key="auto"
- android:title="@string/turn_on_automatically"
- android:summary="@string/turn_on_auto_summary" />
-
- <PreferenceCategory
- android:title="@string/when_night_mode_on">
-
- <SwitchPreference
- android:key="adjust_tint"
- android:title="@string/adjust_tint" />
-
- <SwitchPreference
- android:key="adjust_brightness"
- android:title="@string/adjust_brightness" />
-
- </PreferenceCategory>
-
-</PreferenceScreen>
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 116bc69..b46e862 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -100,13 +100,6 @@
</PreferenceScreen>
- <!--
- <Preference
- android:key="color_transform"
- android:title="@string/color_and_appearance"
- android:fragment="com.android.systemui.tuner.ColorAndAppearanceFragment" />
- -->
-
<PreferenceScreen
android:key="volume_and_do_not_disturb"
android:title="@string/volume_and_do_not_disturb">
diff --git a/packages/SystemUI/src/com/android/systemui/Interpolators.java b/packages/SystemUI/src/com/android/systemui/Interpolators.java
index 17f4dab..fd3bd92 100644
--- a/packages/SystemUI/src/com/android/systemui/Interpolators.java
+++ b/packages/SystemUI/src/com/android/systemui/Interpolators.java
@@ -36,6 +36,7 @@
public static final Interpolator ACCELERATE = new AccelerateInterpolator();
public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator();
public static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f);
+ public static final Interpolator CUSTOM_40_40 = new PathInterpolator(0.4f, 0f, 0.6f, 1f);
/**
* Interpolator to be used when animating a move based on a click. Pair with enough duration.
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 907616c..19ae295 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -47,7 +47,6 @@
Key.QS_DATA_SAVER_DIALOG_SHOWN,
Key.QS_INVERT_COLORS_ADDED,
Key.QS_WORK_ADDED,
- Key.QS_NIGHT_ADDED,
})
public @interface Key {
String OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME = "OverviewLastStackTaskActiveTime";
@@ -67,7 +66,6 @@
String QS_DATA_SAVER_DIALOG_SHOWN = "QsDataSaverDialogShown";
String QS_INVERT_COLORS_ADDED = "QsInvertColorsAdded";
String QS_WORK_ADDED = "QsWorkAdded";
- String QS_NIGHT_ADDED = "QsNightAdded";
}
public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 39a3412..52b5a54 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -55,7 +55,8 @@
com.android.systemui.media.RingtonePlayer.class,
com.android.systemui.keyboard.KeyboardUI.class,
com.android.systemui.tv.pip.PipUI.class,
- com.android.systemui.shortcut.ShortcutKeyDispatcher.class
+ com.android.systemui.shortcut.ShortcutKeyDispatcher.class,
+ com.android.systemui.VendorServices.class
};
/**
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 53c2233..06ce88cc 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -23,8 +23,12 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.R;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.phone.QSTileHost;
@@ -89,7 +93,7 @@
}
public ScrimController createScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
- View headsUpScrim) {
+ View headsUpScrim, LockscreenWallpaper lockscreenWallpaper) {
return new ScrimController(scrimBehind, scrimInFront, headsUpScrim);
}
@@ -115,4 +119,8 @@
public <T> T createInstance(Class<T> classType) {
return null;
}
+
+ public AssistManager createAssistManager(BaseStatusBar bar, Context context) {
+ return new AssistManager(bar, context);
+ }
}
diff --git a/core/java/com/android/internal/app/IEphemeralResolver.aidl b/packages/SystemUI/src/com/android/systemui/VendorServices.java
similarity index 61%
copy from core/java/com/android/internal/app/IEphemeralResolver.aidl
copy to packages/SystemUI/src/com/android/systemui/VendorServices.java
index 40429ee..0be6b12 100644
--- a/core/java/com/android/internal/app/IEphemeralResolver.aidl
+++ b/packages/SystemUI/src/com/android/systemui/VendorServices.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -11,14 +11,19 @@
* 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.
+ * limitations under the License
*/
-package com.android.internal.app;
+package com.android.systemui;
-import android.content.Intent;
-import android.os.IRemoteCallback;
+/**
+ * Placeholder for any vendor-specific services.
+ */
+public class VendorServices extends SystemUI {
-oneway interface IEphemeralResolver {
- void getEphemeralResolveInfoList(IRemoteCallback callback, int digestPrefix, int sequence);
+ @Override
+ public void start() {
+ // no-op
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
index 91f6520..b1f454e 100644
--- a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
+++ b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
@@ -21,6 +21,7 @@
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
+import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
@@ -28,6 +29,7 @@
import android.provider.Settings;
import android.util.Log;
import android.view.MotionEvent;
+import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
@@ -48,6 +50,8 @@
private static final String TAG = "DataCollector";
private static final String COLLECTOR_ENABLE = "data_collector_enable";
private static final String COLLECT_BAD_TOUCHES = "data_collector_collect_bad_touches";
+ private static final String ALLOW_REJECTED_TOUCH_REPORTS =
+ "data_collector_allow_rejected_touch_reports";
private static final long TIMEOUT_MILLIS = 11000; // 11 seconds.
public static final boolean DEBUG = false;
@@ -64,6 +68,7 @@
private boolean mCollectBadTouches = false;
private boolean mCornerSwiping = false;
private boolean mTrackingStarted = false;
+ private boolean mAllowReportRejectedTouch = false;
private static DataCollector sInstance = null;
@@ -87,6 +92,11 @@
mSettingsObserver,
UserHandle.USER_ALL);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(ALLOW_REJECTED_TOUCH_REPORTS), false,
+ mSettingsObserver,
+ UserHandle.USER_ALL);
+
updateConfiguration();
}
@@ -104,10 +114,13 @@
mCollectBadTouches = mEnableCollector && 0 != Settings.Secure.getInt(
mContext.getContentResolver(),
COLLECT_BAD_TOUCHES, 0);
+ mAllowReportRejectedTouch = Build.IS_DEBUGGABLE && 0 != Settings.Secure.getInt(
+ mContext.getContentResolver(),
+ ALLOW_REJECTED_TOUCH_REPORTS, 0);
}
private boolean sessionEntrypoint() {
- if (mEnableCollector && mCurrentSession == null) {
+ if (isEnabled() && mCurrentSession == null) {
onSessionStart();
return true;
}
@@ -115,7 +128,7 @@
}
private void sessionExitpoint(int result) {
- if (mEnableCollector && mCurrentSession != null) {
+ if (mCurrentSession != null) {
onSessionEnd(result);
}
}
@@ -130,8 +143,36 @@
SensorLoggerSession session = mCurrentSession;
mCurrentSession = null;
- session.end(System.currentTimeMillis(), result);
- queueSession(session);
+ if (mEnableCollector) {
+ session.end(System.currentTimeMillis(), result);
+ queueSession(session);
+ }
+ }
+
+ public Uri reportRejectedTouch() {
+ if (mCurrentSession == null) {
+ Toast.makeText(mContext, "Generating rejected touch report failed: session timed out.",
+ Toast.LENGTH_LONG).show();
+ return null;
+ }
+ SensorLoggerSession currentSession = mCurrentSession;
+
+ currentSession.setType(Session.REJECTED_TOUCH_REPORT);
+ currentSession.end(System.currentTimeMillis(), Session.SUCCESS);
+ Session proto = currentSession.toProto();
+
+ byte[] b = Session.toByteArray(proto);
+ File dir = new File(mContext.getExternalCacheDir(), "rejected_touch_reports");
+ dir.mkdir();
+ File touch = new File(dir, "rejected_touch_report_" + System.currentTimeMillis());
+
+ try {
+ new FileOutputStream(touch).write(b);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ return Uri.fromFile(touch);
}
private void queueSession(final SensorLoggerSession currentSession) {
@@ -164,7 +205,7 @@
@Override
public synchronized void onSensorChanged(SensorEvent event) {
- if (mEnableCollector && mCurrentSession != null) {
+ if (isEnabled() && mCurrentSession != null) {
mCurrentSession.addSensorEvent(event, System.nanoTime());
enforceTimeout();
}
@@ -186,7 +227,19 @@
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
+ /**
+ * @return true if data is being collected - either for data gathering or creating a
+ * rejected touch report.
+ */
public boolean isEnabled() {
+ return mEnableCollector || mAllowReportRejectedTouch;
+ }
+
+ /**
+ * @return true if the full data set for data gathering should be collected - including
+ * extensive sensor data, which is is not normally included with rejected touch reports.
+ */
+ public boolean isEnabledFull() {
return mEnableCollector;
}
@@ -401,8 +454,12 @@
}
private void addEvent(int eventType) {
- if (mEnableCollector && mCurrentSession != null) {
+ if (isEnabled() && mCurrentSession != null) {
mCurrentSession.addPhoneEvent(eventType, System.nanoTime());
}
}
+
+ public boolean isReportingEnabled() {
+ return mAllowReportRejectedTouch;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java b/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java
index faaad2b..b39803a 100644
--- a/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java
+++ b/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java
@@ -53,6 +53,10 @@
mType = Session.REAL;
}
+ public void setType(int type) {
+ mType = type;
+ }
+
public void end(long endTimestampMillis, int result) {
mResult = result;
mEndTimestampMillis = endTimestampMillis;
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
index 5878219..8bd38db 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
@@ -95,39 +95,34 @@
private class AssistDisclosureView extends View
implements ValueAnimator.AnimatorUpdateListener {
- public static final int TRACING_ANIMATION_DURATION = 600;
- public static final int ALPHA_IN_ANIMATION_DURATION = 450;
- public static final int ALPHA_OUT_ANIMATION_DURATION = 400;
+ static final int FULL_ALPHA = 222; // 87%
+ static final int ALPHA_IN_ANIMATION_DURATION = 400;
+ static final int ALPHA_OUT_ANIMATION_DURATION = 300;
+
private float mThickness;
private float mShadowThickness;
private final Paint mPaint = new Paint();
private final Paint mShadowPaint = new Paint();
- private final ValueAnimator mTracingAnimator;
private final ValueAnimator mAlphaOutAnimator;
private final ValueAnimator mAlphaInAnimator;
private final AnimatorSet mAnimator;
- private float mTracingProgress = 0;
private int mAlpha = 0;
public AssistDisclosureView(Context context) {
super(context);
- mTracingAnimator = ValueAnimator.ofFloat(0, 1).setDuration(TRACING_ANIMATION_DURATION);
- mTracingAnimator.addUpdateListener(this);
- mTracingAnimator.setInterpolator(AnimationUtils.loadInterpolator(mContext,
- R.interpolator.assist_disclosure_trace));
- mAlphaInAnimator = ValueAnimator.ofInt(0, 255).setDuration(ALPHA_IN_ANIMATION_DURATION);
+ mAlphaInAnimator = ValueAnimator.ofInt(0, FULL_ALPHA)
+ .setDuration(ALPHA_IN_ANIMATION_DURATION);
mAlphaInAnimator.addUpdateListener(this);
- mAlphaInAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- mAlphaOutAnimator = ValueAnimator.ofInt(255, 0).setDuration(
+ mAlphaInAnimator.setInterpolator(Interpolators.CUSTOM_40_40);
+ mAlphaOutAnimator = ValueAnimator.ofInt(FULL_ALPHA, 0).setDuration(
ALPHA_OUT_ANIMATION_DURATION);
mAlphaOutAnimator.addUpdateListener(this);
- mAlphaOutAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+ mAlphaOutAnimator.setInterpolator(Interpolators.CUSTOM_40_40);
mAnimator = new AnimatorSet();
- mAnimator.play(mAlphaInAnimator).with(mTracingAnimator);
mAnimator.play(mAlphaInAnimator).before(mAlphaOutAnimator);
mAnimator.addListener(new AnimatorListenerAdapter() {
boolean mCancelled;
@@ -174,8 +169,6 @@
super.onDetachedFromWindow();
mAnimator.cancel();
-
- mTracingProgress = 0;
mAlpha = 0;
}
@@ -197,45 +190,32 @@
final int width = getWidth();
final int height = getHeight();
float thickness = mThickness;
- final float pixelProgress = mTracingProgress * (width + height - 2 * thickness);
- float bottomProgress = Math.min(pixelProgress, width / 2f);
- if (bottomProgress > 0) {
- drawBeam(canvas,
- width / 2f - bottomProgress,
- height - thickness,
- width / 2f + bottomProgress,
- height, paint, padding);
- }
+ // bottom
+ drawBeam(canvas,
+ 0,
+ height - thickness,
+ width,
+ height, paint, padding);
- float sideProgress = Math.min(pixelProgress - bottomProgress, height - thickness);
- if (sideProgress > 0) {
- drawBeam(canvas,
- 0,
- (height - thickness) - sideProgress,
- thickness,
- height - thickness, paint, padding);
- drawBeam(canvas,
- width - thickness,
- (height - thickness) - sideProgress,
- width,
- height - thickness, paint, padding);
- }
+ // sides
+ drawBeam(canvas,
+ 0,
+ 0,
+ thickness,
+ height - thickness, paint, padding);
+ drawBeam(canvas,
+ width - thickness,
+ 0,
+ width,
+ height - thickness, paint, padding);
- float topProgress = Math.min(pixelProgress - bottomProgress - sideProgress,
- width / 2 - thickness);
- if (sideProgress > 0 && topProgress > 0) {
- drawBeam(canvas,
- thickness,
- 0,
- thickness + topProgress,
- thickness, paint, padding);
- drawBeam(canvas,
- (width - thickness) - topProgress,
- 0,
- width - thickness,
- thickness, paint, padding);
- }
+ // top
+ drawBeam(canvas,
+ thickness,
+ 0,
+ width - thickness,
+ thickness, paint, padding);
}
private void drawBeam(Canvas canvas, float left, float top, float right, float bottom,
@@ -253,8 +233,6 @@
mAlpha = (int) mAlphaOutAnimator.getAnimatedValue();
} else if (animation == mAlphaInAnimator) {
mAlpha = (int) mAlphaInAnimator.getAnimatedValue();
- } else if (animation == mTracingAnimator) {
- mTracingProgress = (float) mTracingAnimator.getAnimatedValue();
}
invalidate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index a0aeb2f..9eceeac 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -15,6 +15,7 @@
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
@@ -28,6 +29,7 @@
import android.widget.ImageView;
import com.android.internal.app.AssistUtils;
+import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
@@ -45,13 +47,13 @@
private static final long TIMEOUT_SERVICE = 2500;
private static final long TIMEOUT_ACTIVITY = 1000;
- private final Context mContext;
+ protected final Context mContext;
private final WindowManager mWindowManager;
private final AssistDisclosure mAssistDisclosure;
private AssistOrbContainer mView;
private final BaseStatusBar mBar;
- private final AssistUtils mAssistUtils;
+ protected final AssistUtils mAssistUtils;
private IVoiceInteractionSessionShowCallback mShowCallback =
new IVoiceInteractionSessionShowCallback.Stub() {
@@ -81,6 +83,23 @@
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mAssistUtils = new AssistUtils(context);
mAssistDisclosure = new AssistDisclosure(context, new Handler());
+
+ registerVoiceInteractionSessionListener();
+ }
+
+ protected void registerVoiceInteractionSessionListener() {
+ mAssistUtils.registerVoiceInteractionSessionListener(
+ new IVoiceInteractionSessionListener.Stub() {
+ @Override
+ public void onVoiceSessionShown() throws RemoteException {
+ Log.v(TAG, "Voice open");
+ }
+
+ @Override
+ public void onVoiceSessionHidden() throws RemoteException {
+ Log.v(TAG, "Voice closed");
+ }
+ });
}
public void onConfigurationChanged() {
@@ -103,6 +122,10 @@
}
}
+ protected boolean shouldShowOrb() {
+ return true;
+ }
+
public void startAssist(Bundle args) {
final ComponentName assistComponent = getAssistInfo();
if (assistComponent == null) {
@@ -110,7 +133,7 @@
}
final boolean isService = assistComponent.equals(getVoiceInteractorComponentName());
- if (!isService || !isVoiceSessionRunning()) {
+ if (!isService || (!isVoiceSessionRunning() && shouldShowOrb())) {
showOrb(assistComponent, isService);
mView.postDelayed(mHideRunnable, isService
? TIMEOUT_SERVICE
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java
index bad739fd..4585fe9 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java
@@ -62,36 +62,36 @@
@Override
public float getFalseTouchEvaluation(int type, Stroke stroke) {
Data data = mStrokeMap.get(stroke);
- return SpeedRatioEvaluator.evaluate(data.maxSpeedRatio)
- + DistanceRatioEvaluator.evaluate(data.maxDistanceRatio);
+ return 2 * SpeedRatioEvaluator.evaluate(data.maxSpeedRatio);
}
private static class Data {
- public Point previousPoint;
- public float previousSpeed;
- public float previousDistance;
- public float maxSpeedRatio;
- public float maxDistanceRatio;
+
+ static final float MILLIS_TO_NANOS = 1e6f;
+
+ Point previousPoint;
+ float previousSpeed = 0;
+ float maxSpeedRatio = 0;
public Data(Point point) {
previousPoint = point;
- previousSpeed = previousDistance = 0.0f;
- maxDistanceRatio = maxSpeedRatio = 0.0f;
}
public void addPoint(Point point) {
float distance = previousPoint.dist(point);
float duration = (float) (point.timeOffsetNano - previousPoint.timeOffsetNano + 1);
float speed = distance / duration;
- if (previousDistance != 0.0f) {
- maxDistanceRatio = Math.max(maxDistanceRatio, distance / previousDistance);
- }
+ if (duration > 20 * MILLIS_TO_NANOS || duration < 5 * MILLIS_TO_NANOS) {
+ // reject this segment and ensure we won't use data about it in the next round.
+ previousSpeed = 0;
+ previousPoint = point;
+ return;
+ }
if (previousSpeed != 0.0f) {
maxSpeedRatio = Math.max(maxSpeedRatio, speed / previousSpeed);
}
- previousDistance = distance;
previousSpeed = speed;
previousPoint = point;
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceRatioEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceRatioEvaluator.java
deleted file mode 100644
index 8acb009..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/DistanceRatioEvaluator.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.classifier;
-
-public class DistanceRatioEvaluator {
- public static float evaluate(float value) {
- float evaluation = 0.0f;
- if (value <= 1.0) evaluation++;
- if (value <= 0.5) evaluation++;
- if (value > 4.0) evaluation++;
- if (value > 7.0) evaluation++;
- if (value > 14.0) evaluation++;
- return evaluation;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index 1ac5992..664e886 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -22,6 +22,7 @@
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.net.Uri;
import android.os.Handler;
import android.os.PowerManager;
import android.os.UserHandle;
@@ -142,7 +143,7 @@
if (mHumanInteractionClassifier.isEnabled()) {
registerSensors(CLASSIFIER_SENSORS);
}
- if (mDataCollector.isEnabled()) {
+ if (mDataCollector.isEnabledFull()) {
registerSensors(COLLECTOR_SENSORS);
}
}
@@ -400,4 +401,15 @@
pw.print("mScreenOn="); pw.println(mScreenOn ? 1 : 0);
pw.println();
}
+
+ public Uri reportRejectedTouch() {
+ if (mDataCollector.isEnabled()) {
+ return mDataCollector.reportRejectedTouch();
+ }
+ return null;
+ }
+
+ public boolean isReportingEnabled() {
+ return mDataCollector.isReportingEnabled();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HistoryEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/HistoryEvaluator.java
index 85a9bee..4c64711 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/HistoryEvaluator.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/HistoryEvaluator.java
@@ -16,6 +16,8 @@
package com.android.systemui.classifier;
+import android.os.SystemClock;
+
import java.util.ArrayList;
/**
@@ -31,7 +33,7 @@
private long mLastUpdate;
public HistoryEvaluator() {
- mLastUpdate = System.currentTimeMillis();
+ mLastUpdate = SystemClock.elapsedRealtime();
}
public void addStroke(float evaluation) {
@@ -69,15 +71,18 @@
}
private void decayValue() {
- long currentTimeMillis = System.currentTimeMillis();
+ long time = SystemClock.elapsedRealtime();
+
+ if (time <= mLastUpdate) {
+ return;
+ }
// All weights are multiplied by HISTORY_FACTOR after each INTERVAL milliseconds.
- float factor = (float) Math.pow(HISTORY_FACTOR,
- (float) (currentTimeMillis - mLastUpdate) / INTERVAL);
+ float factor = (float) Math.pow(HISTORY_FACTOR, (time - mLastUpdate) / INTERVAL);
decayValue(mStrokes, factor);
decayValue(mGestureWeights, factor);
- mLastUpdate = currentTimeMillis;
+ mLastUpdate = time;
}
private void decayValue(ArrayList<Data> list, float factor) {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java
index 4c6cea0..e34f222 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java
@@ -19,6 +19,7 @@
public class SpeedRatioEvaluator {
public static float evaluate(float value) {
float evaluation = 0.0f;
+ if (value == 0) return 0;
if (value <= 1.0) evaluation++;
if (value <= 0.5) evaluation++;
if (value > 9.0) evaluation++;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 9eb768c..874021a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -33,7 +33,7 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final boolean ENABLED = true;
private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50;
- private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
+ static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
private static final int PULSE_REASONS = 4;
@@ -57,8 +57,9 @@
private static SummaryStats sEmergencyCallStats;
private static SummaryStats[][] sProxStats; // [reason][near/far]
- public static void tracePickupPulse(boolean withinVibrationThreshold) {
+ public static void tracePickupPulse(Context context, boolean withinVibrationThreshold) {
if (!ENABLED) return;
+ init(context);
log("pickupPulse withinVibrationThreshold=" + withinVibrationThreshold);
(withinVibrationThreshold ? sPickupPulseNearVibrationStats
: sPickupPulseNotNearVibrationStats).append();
@@ -76,8 +77,9 @@
log("pulseFinish");
}
- public static void traceNotificationPulse(long instance) {
+ public static void traceNotificationPulse(Context context, long instance) {
if (!ENABLED) return;
+ init(context);
log("notificationPulse instance=" + instance);
sNotificationPulseStats.append();
}
@@ -153,9 +155,9 @@
public static void traceProximityResult(Context context, boolean near, long millis,
int pulseReason) {
if (!ENABLED) return;
+ init(context);
log("proximityResult reason=" + pulseReasonToString(pulseReason) + " near=" + near
+ " millis=" + millis);
- init(context);
sProxStats[pulseReason][near ? 0 : 1].append();
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 3370091..ec4f447 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -16,8 +16,6 @@
package com.android.systemui.doze;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
import android.app.UiModeManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -39,9 +37,10 @@
import android.util.Log;
import android.view.Display;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.DozeParameters.PulseSchedule;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -53,19 +52,6 @@
private static final String ACTION_BASE = "com.android.systemui.doze";
private static final String PULSE_ACTION = ACTION_BASE + ".pulse";
- private static final String NOTIFICATION_PULSE_ACTION = ACTION_BASE + ".notification_pulse";
- private static final String EXTRA_INSTANCE = "instance";
-
- /**
- * Earliest time we pulse due to a notification light after the service started.
- *
- * <p>Incoming notification light events during the blackout period are
- * delayed to the earliest time defined by this constant.</p>
- *
- * <p>This delay avoids a pulse immediately after screen off, at which
- * point the notification light is re-enabled again by NoMan.</p>
- */
- private static final int EARLIEST_LIGHT_PULSE_AFTER_START_MS = 10 * 1000;
private final String mTag = String.format(TAG + ".%08x", hashCode());
private final Context mContext = this;
@@ -78,19 +64,14 @@
private TriggerSensor mPickupSensor;
private PowerManager mPowerManager;
private PowerManager.WakeLock mWakeLock;
- private AlarmManager mAlarmManager;
private UiModeManager mUiModeManager;
private boolean mDreaming;
private boolean mPulsing;
private boolean mBroadcastReceiverRegistered;
private boolean mDisplayStateSupported;
- private boolean mNotificationLightOn;
private boolean mPowerSaveActive;
private boolean mCarMode;
private long mNotificationPulseTime;
- private long mLastScheduleResetTime;
- private long mEarliestPulseDueToLight;
- private int mScheduleResetsRemaining;
public DozeService() {
if (DEBUG) Log.d(mTag, "new DozeService()");
@@ -108,11 +89,11 @@
pw.print(" mSigMotionSensor: "); pw.println(mSigMotionSensor);
pw.print(" mPickupSensor:"); pw.println(mPickupSensor);
pw.print(" mDisplayStateSupported: "); pw.println(mDisplayStateSupported);
- pw.print(" mNotificationLightOn: "); pw.println(mNotificationLightOn);
pw.print(" mPowerSaveActive: "); pw.println(mPowerSaveActive);
pw.print(" mCarMode: "); pw.println(mCarMode);
- pw.print(" mNotificationPulseTime: "); pw.println(mNotificationPulseTime);
- pw.print(" mScheduleResetsRemaining: "); pw.println(mScheduleResetsRemaining);
+ pw.print(" mNotificationPulseTime: "); pw.println(
+ DozeLog.FORMAT.format(new Date(mNotificationPulseTime
+ - SystemClock.elapsedRealtime() + System.currentTimeMillis())));
mDozeParameters.dump(pw);
}
@@ -139,7 +120,6 @@
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mWakeLock.setReferenceCounted(true);
- mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
mDisplayStateSupported = mDozeParameters.getDisplayStateSupported();
mUiModeManager = (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
turnDisplayOff();
@@ -174,8 +154,6 @@
}
mDreaming = true;
- rescheduleNotificationPulse(false /*predicate*/); // cancel any pending pulse alarms
- mEarliestPulseDueToLight = System.currentTimeMillis() + EARLIEST_LIGHT_PULSE_AFTER_START_MS;
listenForPulseSignals(true);
// Ask the host to get things ready to start dozing.
@@ -212,6 +190,10 @@
}
private void requestPulse(final int reason) {
+ requestPulse(reason, false /* performedProxCheck */);
+ }
+
+ private void requestPulse(final int reason, boolean performedProxCheck) {
if (mHost != null && mDreaming && !mPulsing) {
// Let the host know we want to pulse. Wait for it to be ready, then
// turn the screen on. When finished, turn the screen off again.
@@ -224,10 +206,9 @@
return;
}
final long start = SystemClock.uptimeMillis();
- final boolean nonBlocking = reason == DozeLog.PULSE_REASON_SENSOR_PICKUP
- && mDozeParameters.getPickupPerformsProxCheck();
- if (nonBlocking) {
- // proximity check is only done to capture statistics, continue pulsing
+ if (performedProxCheck) {
+ // the caller already performed a successful proximity check; we'll only do one to
+ // capture statistics, continue pulsing immediately.
continuePulsing(reason);
}
// perform a proximity check
@@ -237,7 +218,7 @@
final boolean isNear = result == RESULT_NEAR;
final long end = SystemClock.uptimeMillis();
DozeLog.traceProximityResult(mContext, isNear, end - start, reason);
- if (nonBlocking) {
+ if (performedProxCheck) {
// we already continued
return;
}
@@ -311,7 +292,6 @@
private void listenForBroadcasts(boolean listen) {
if (listen) {
final IntentFilter filter = new IntentFilter(PULSE_ACTION);
- filter.addAction(NOTIFICATION_PULSE_ACTION);
filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE);
mContext.registerReceiver(mBroadcastReceiver, filter);
mBroadcastReceiverRegistered = true;
@@ -325,93 +305,17 @@
private void listenForNotifications(boolean listen) {
if (listen) {
- resetNotificationResets();
mHost.addCallback(mHostCallback);
-
- // Continue to pulse for existing LEDs.
- mNotificationLightOn = mHost.isNotificationLightOn();
- if (mNotificationLightOn) {
- updateNotificationPulseDueToLight();
- }
} else {
mHost.removeCallback(mHostCallback);
}
}
- private void resetNotificationResets() {
- if (DEBUG) Log.d(mTag, "resetNotificationResets");
- mScheduleResetsRemaining = mDozeParameters.getPulseScheduleResets();
- }
-
- private void updateNotificationPulseDueToLight() {
- long timeMs = System.currentTimeMillis();
- timeMs = Math.max(timeMs, mEarliestPulseDueToLight);
- updateNotificationPulse(timeMs);
- }
-
- private void updateNotificationPulse(long notificationTimeMs) {
- if (DEBUG) Log.d(mTag, "updateNotificationPulse notificationTimeMs=" + notificationTimeMs);
+ private void requestNotificationPulse() {
+ if (DEBUG) Log.d(mTag, "requestNotificationPulse");
if (!mDozeParameters.getPulseOnNotifications()) return;
- if (mScheduleResetsRemaining <= 0) {
- if (DEBUG) Log.d(mTag, "No more schedule resets remaining");
- return;
- }
- final long pulseDuration = mDozeParameters.getPulseDuration(false /*pickup*/);
- boolean pulseImmediately = System.currentTimeMillis() >= notificationTimeMs;
- if ((notificationTimeMs - mLastScheduleResetTime) >= pulseDuration) {
- mScheduleResetsRemaining--;
- mLastScheduleResetTime = notificationTimeMs;
- } else if (!pulseImmediately){
- if (DEBUG) Log.d(mTag, "Recently updated, not resetting schedule");
- return;
- }
- if (DEBUG) Log.d(mTag, "mScheduleResetsRemaining = " + mScheduleResetsRemaining);
- mNotificationPulseTime = notificationTimeMs;
- if (pulseImmediately) {
- DozeLog.traceNotificationPulse(0);
- requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
- }
- // schedule the rest of the pulses
- rescheduleNotificationPulse(true /*predicate*/);
- }
-
- private PendingIntent notificationPulseIntent(long instance) {
- return PendingIntent.getBroadcast(mContext, 0,
- new Intent(NOTIFICATION_PULSE_ACTION)
- .setPackage(getPackageName())
- .putExtra(EXTRA_INSTANCE, instance)
- .setFlags(Intent.FLAG_RECEIVER_FOREGROUND),
- PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- private void rescheduleNotificationPulse(boolean predicate) {
- if (DEBUG) Log.d(mTag, "rescheduleNotificationPulse predicate=" + predicate);
- final PendingIntent notificationPulseIntent = notificationPulseIntent(0);
- mAlarmManager.cancel(notificationPulseIntent);
- if (!predicate) {
- if (DEBUG) Log.d(mTag, " don't reschedule: predicate is false");
- return;
- }
- final PulseSchedule schedule = mDozeParameters.getPulseSchedule();
- if (schedule == null) {
- if (DEBUG) Log.d(mTag, " don't reschedule: schedule is null");
- return;
- }
- final long now = System.currentTimeMillis();
- final long time = schedule.getNextTime(now, mNotificationPulseTime);
- if (time <= 0) {
- if (DEBUG) Log.d(mTag, " don't reschedule: time is " + time);
- return;
- }
- final long delta = time - now;
- if (delta <= 0) {
- if (DEBUG) Log.d(mTag, " don't reschedule: delta is " + delta);
- return;
- }
- final long instance = time - mNotificationPulseTime;
- if (DEBUG) Log.d(mTag, "Scheduling pulse " + instance + " in " + delta + "ms for "
- + new Date(time));
- mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, time, notificationPulseIntent(instance));
+ mNotificationPulseTime = SystemClock.elapsedRealtime();
+ requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
}
private static String triggerEventToString(TriggerEvent event) {
@@ -434,13 +338,6 @@
if (DEBUG) Log.d(mTag, "Received pulse intent");
requestPulse(DozeLog.PULSE_REASON_INTENT);
}
- if (NOTIFICATION_PULSE_ACTION.equals(intent.getAction())) {
- final long instance = intent.getLongExtra(EXTRA_INSTANCE, -1);
- if (DEBUG) Log.d(mTag, "Received notification pulse intent instance=" + instance);
- DozeLog.traceNotificationPulse(instance);
- requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
- rescheduleNotificationPulse(mNotificationLightOn);
- }
if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
mCarMode = true;
if (mCarMode && mDreaming) {
@@ -460,17 +357,13 @@
@Override
public void onBuzzBeepBlinked() {
if (DEBUG) Log.d(mTag, "onBuzzBeepBlinked");
- updateNotificationPulse(System.currentTimeMillis());
+ requestNotificationPulse();
}
@Override
public void onNotificationLight(boolean on) {
- if (DEBUG) Log.d(mTag, "onNotificationLight on=" + on);
- if (mNotificationLightOn == on) return;
- mNotificationLightOn = on;
- if (mNotificationLightOn) {
- updateNotificationPulseDueToLight();
- }
+ if (DEBUG) Log.d(mTag, "onNotificationLight (noop) on=" + on);
+ // noop for now
}
@Override
@@ -538,6 +431,13 @@
mWakeLock.acquire();
try {
if (DEBUG) Log.d(mTag, "onTrigger: " + triggerEventToString(event));
+ boolean sensorPerformsProxCheck = false;
+ if (mSensor.getType() == Sensor.TYPE_PICK_UP_GESTURE) {
+ int subType = (int) event.values[0];
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_AMBIENT_GESTURE, subType);
+ sensorPerformsProxCheck = mDozeParameters.getPickupSubtypePerformsProxCheck(
+ subType);
+ }
if (mDebugVibrate) {
final Vibrator v = (Vibrator) mContext.getSystemService(
Context.VIBRATOR_SERVICE);
@@ -549,22 +449,17 @@
}
mRegistered = false;
- requestPulse(mPulseReason);
+ requestPulse(mPulseReason, sensorPerformsProxCheck);
updateListener(); // reregister, this sensor only fires once
- // reset the notification pulse schedule, but only if we think we were not triggered
- // by a notification-related vibration
- final long timeSinceNotification = System.currentTimeMillis()
+ // record pickup gesture, also keep track of whether we might have been triggered
+ // by recent vibration.
+ final long timeSinceNotification = SystemClock.elapsedRealtime()
- mNotificationPulseTime;
final boolean withinVibrationThreshold =
timeSinceNotification < mDozeParameters.getPickupVibrationThreshold();
- if (withinVibrationThreshold) {
- if (DEBUG) Log.d(mTag, "Not resetting schedule, recent notification");
- } else {
- resetNotificationResets();
- }
if (mSensor.getType() == Sensor.TYPE_PICK_UP_GESTURE) {
- DozeLog.tracePickupPulse(withinVibrationThreshold);
+ DozeLog.tracePickupPulse(mContext, withinVibrationThreshold);
}
} finally {
mWakeLock.release();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 84d3599..84901ee 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -23,6 +23,7 @@
import android.os.Debug;
import android.os.IBinder;
import android.os.Process;
+import android.os.Trace;
import android.util.Log;
import com.android.internal.policy.IKeyguardDrawnCallback;
@@ -73,27 +74,33 @@
@Override // Binder interface
public void verifyUnlock(IKeyguardExitCallback callback) {
+ Trace.beginSection("KeyguardService.mBinder#verifyUnlock");
checkPermission();
mKeyguardViewMediator.verifyUnlock(callback);
+ Trace.endSection();
}
@Override // Binder interface
public void keyguardDone(boolean authenticated, boolean wakeup) {
+ Trace.beginSection("KeyguardService.mBinder#keyguardDone");
checkPermission();
// TODO: Remove wakeup
mKeyguardViewMediator.keyguardDone(authenticated);
+ Trace.endSection();
}
@Override // Binder interface
public void setOccluded(boolean isOccluded) {
+ Trace.beginSection("KeyguardService.mBinder#setOccluded");
checkPermission();
mKeyguardViewMediator.setOccluded(isOccluded);
+ Trace.endSection();
}
@Override // Binder interface
- public void dismiss() {
+ public void dismiss(boolean allowWhileOccluded) {
checkPermission();
- mKeyguardViewMediator.dismiss();
+ mKeyguardViewMediator.dismiss(allowWhileOccluded);
}
@Override // Binder interface
@@ -122,20 +129,26 @@
@Override // Binder interface
public void onStartedWakingUp() {
+ Trace.beginSection("KeyguardService.mBinder#onStartedWakingUp");
checkPermission();
mKeyguardViewMediator.onStartedWakingUp();
+ Trace.endSection();
}
@Override // Binder interface
public void onScreenTurningOn(IKeyguardDrawnCallback callback) {
+ Trace.beginSection("KeyguardService.mBinder#onScreenTurningOn");
checkPermission();
mKeyguardViewMediator.onScreenTurningOn(callback);
+ Trace.endSection();
}
@Override // Binder interface
public void onScreenTurnedOn() {
+ Trace.beginSection("KeyguardService.mBinder#onScreenTurningOn");
checkPermission();
mKeyguardViewMediator.onScreenTurnedOn();
+ Trace.endSection();
}
@Override // Binder interface
@@ -152,8 +165,10 @@
@Override // Binder interface
public void onSystemReady() {
+ Trace.beginSection("KeyguardService.mBinder#onSystemReady");
checkPermission();
mKeyguardViewMediator.onSystemReady();
+ Trace.endSection();
}
@Override // Binder interface
@@ -176,8 +191,10 @@
@Override
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+ Trace.beginSection("KeyguardService.mBinder#startKeyguardExitAnimation");
checkPermission();
mKeyguardViewMediator.startKeyguardExitAnimation(startTime, fadeoutDuration);
+ Trace.endSection();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 6759e6b..de0c77b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -16,7 +16,11 @@
package com.android.systemui.keyguard;
-import android.annotation.UserIdInt;
+import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
+
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
@@ -42,6 +46,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -81,13 +86,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.List;
-
-import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_WRONG_CREDENTIAL;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
/**
* Mediates requests related to the keyguard. This includes queries about the
@@ -360,9 +358,9 @@
mSwitchingUser = false;
if (userId != UserHandle.USER_SYSTEM) {
UserInfo info = UserManager.get(mContext).getUserInfo(userId);
- if (info != null && info.isGuest()) {
+ if (info != null && (info.isGuest() || info.isDemo())) {
// If we just switched to a guest, try to dismiss keyguard.
- dismiss();
+ dismiss(false /* allowWhileOccluded */);
}
}
}
@@ -399,8 +397,7 @@
sendUserPresentBroadcast();
synchronized (KeyguardViewMediator.this) {
// If system user is provisioned, we might want to lock now to avoid showing launcher
- if (UserManager.isSplitSystemUser()
- && KeyguardUpdateMonitor.getCurrentUser() == UserHandle.USER_SYSTEM) {
+ if (mustNotUnlockCurrentUser()) {
doKeyguardLocked(null);
}
}
@@ -500,6 +497,22 @@
userId);
}
}
+
+ @Override
+ public void onTrustChanged(int userId) {
+ if (userId == KeyguardUpdateMonitor.getCurrentUser()) {
+ synchronized (KeyguardViewMediator.this) {
+ notifyTrustedChangedLocked(mUpdateMonitor.getUserHasTrust(userId));
+ }
+ }
+ }
+
+ @Override
+ public void onHasLockscreenWallpaperChanged(boolean hasLockscreenWallpaper) {
+ synchronized (KeyguardViewMediator.this) {
+ notifyHasLockscreenWallpaperChanged(hasLockscreenWallpaper);
+ }
+ }
};
ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() {
@@ -521,7 +534,9 @@
@Override
public void keyguardDoneDrawing() {
+ Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardDoneDrawing");
mHandler.sendEmptyMessage(KEYGUARD_DONE_DRAWING);
+ Trace.endSection();
}
@Override
@@ -531,6 +546,7 @@
@Override
public void keyguardDonePending(boolean strongAuth) {
+ Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardDonePending");
mKeyguardDonePending = true;
mHideAnimationRun = true;
mStatusBarKeyguardViewManager.startPreHideAnimation(null /* finishRunnable */);
@@ -539,20 +555,25 @@
if (strongAuth) {
mUpdateMonitor.reportSuccessfulStrongAuthUnlockAttempt();
}
+ Trace.endSection();
}
@Override
public void keyguardGone() {
+ Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardGone");
mKeyguardDisplayManager.hide();
+ Trace.endSection();
}
@Override
public void readyForKeyguardDone() {
+ Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#readyForKeyguardDone");
if (mKeyguardDonePending) {
// Somebody has called keyguardDonePending before, which means that we are
// authenticated
KeyguardViewMediator.this.keyguardDone(true /* authenticated */);
}
+ Trace.endSection();
}
@Override
@@ -595,11 +616,7 @@
return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
} else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0) {
return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT;
- } else if (trust && (strongAuth & SOME_AUTH_REQUIRED_AFTER_WRONG_CREDENTIAL) != 0) {
- return KeyguardSecurityView.PROMPT_REASON_WRONG_CREDENTIAL;
}
-
-
return KeyguardSecurityView.PROMPT_REASON_NONE;
}
};
@@ -608,6 +625,11 @@
mPM.userActivity(SystemClock.uptimeMillis(), false);
}
+ boolean mustNotUnlockCurrentUser() {
+ return (UserManager.isSplitSystemUser() || UserManager.isDeviceInDemoMode(mContext))
+ && KeyguardUpdateMonitor.getCurrentUser() == UserHandle.USER_SYSTEM;
+ }
+
private void setupLocked() {
mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWM = WindowManagerGlobal.getWindowManagerService();
@@ -889,6 +911,7 @@
* Let's us know when the device is waking up.
*/
public void onStartedWakingUp() {
+ Trace.beginSection("KeyguardViewMediator#onStartedWakingUp");
// TODO: Rename all screen off/on references to interactive/sleeping
synchronized (this) {
@@ -900,15 +923,20 @@
}
KeyguardUpdateMonitor.getInstance(mContext).dispatchStartedWakingUp();
maybeSendUserPresentBroadcast();
+ Trace.endSection();
}
public void onScreenTurningOn(IKeyguardDrawnCallback callback) {
+ Trace.beginSection("KeyguardViewMediator#onScreenTurningOn");
notifyScreenOn(callback);
+ Trace.endSection();
}
public void onScreenTurnedOn() {
+ Trace.beginSection("KeyguardViewMediator#onScreenTurnedOn");
notifyScreenTurnedOn();
mUpdateMonitor.dispatchScreenTurnedOn();
+ Trace.endSection();
}
public void onScreenTurnedOff() {
@@ -1022,6 +1050,7 @@
* @see android.app.KeyguardManager#exitKeyguardSecurely
*/
public void verifyUnlock(IKeyguardExitCallback callback) {
+ Trace.beginSection("KeyguardViewMediator#verifyUnlock");
synchronized (this) {
if (DEBUG) Log.d(TAG, "verifyUnlock");
if (shouldWaitForProvisioning()) {
@@ -1072,6 +1101,7 @@
}
}
}
+ Trace.endSection();
}
/**
@@ -1085,16 +1115,19 @@
* Notify us when the keyguard is occluded by another window
*/
public void setOccluded(boolean isOccluded) {
+ Trace.beginSection("KeyguardViewMediator#setOccluded");
if (DEBUG) Log.d(TAG, "setOccluded " + isOccluded);
mHandler.removeMessages(SET_OCCLUDED);
Message msg = mHandler.obtainMessage(SET_OCCLUDED, (isOccluded ? 1 : 0), 0);
mHandler.sendMessage(msg);
+ Trace.endSection();
}
/**
* Handles SET_OCCLUDED message sent by setOccluded()
*/
private void handleSetOccluded(boolean isOccluded) {
+ Trace.beginSection("KeyguardViewMediator#handleSetOccluded");
synchronized (KeyguardViewMediator.this) {
if (mHiding && isOccluded) {
// We're in the process of going away but WindowManager wants to show a
@@ -1109,6 +1142,7 @@
adjustStatusBarLocked();
}
}
+ Trace.endSection();
}
/**
@@ -1181,8 +1215,7 @@
}
// In split system user mode, we never unlock system user.
- if (!UserManager.isSplitSystemUser()
- || KeyguardUpdateMonitor.getCurrentUser() != UserHandle.USER_SYSTEM
+ if (!mustNotUnlockCurrentUser()
|| !mUpdateMonitor.isDeviceProvisioned()) {
// if the setup wizard hasn't run yet, don't show
@@ -1230,15 +1263,16 @@
/**
* Dismiss the keyguard through the security layers.
+ * @param allowWhileOccluded if true, dismiss the keyguard even if it's currently occluded.
*/
- public void handleDismiss() {
- if (mShowing && !mOccluded) {
+ public void handleDismiss(boolean allowWhileOccluded) {
+ if (mShowing && (allowWhileOccluded || !mOccluded)) {
mStatusBarKeyguardViewManager.dismiss();
}
}
- public void dismiss() {
- mHandler.sendEmptyMessage(DISMISS);
+ public void dismiss(boolean allowWhileOccluded) {
+ mHandler.obtainMessage(DISMISS, allowWhileOccluded ? 1 : 0, 0).sendToTarget();
}
/**
@@ -1298,11 +1332,13 @@
* @see #handleShow
*/
private void showLocked(Bundle options) {
+ Trace.beginSection("KeyguardViewMediator#showLocked aqcuiring mShowKeyguardWakeLock");
if (DEBUG) Log.d(TAG, "showLocked");
// ensure we stay awake until we are finished displaying the keyguard
mShowKeyguardWakeLock.acquire();
Message msg = mHandler.obtainMessage(SHOW, options);
mHandler.sendMessage(msg);
+ Trace.endSection();
}
/**
@@ -1310,9 +1346,11 @@
* @see #handleHide()
*/
private void hideLocked() {
+ Trace.beginSection("KeyguardViewMediator#hideLocked");
if (DEBUG) Log.d(TAG, "hideLocked");
Message msg = mHandler.obtainMessage(HIDE);
mHandler.sendMessage(msg);
+ Trace.endSection();
}
public boolean isSecure() {
@@ -1328,6 +1366,9 @@
*/
public void setCurrentUser(int newUserId) {
KeyguardUpdateMonitor.setCurrentUser(newUserId);
+ synchronized (this) {
+ notifyTrustedChangedLocked(mUpdateMonitor.getUserHasTrust(newUserId));
+ }
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -1357,10 +1398,13 @@
};
public void keyguardDone(boolean authenticated) {
+ Trace.beginSection("KeyguardViewMediator#keyguardDone");
if (DEBUG) Log.d(TAG, "keyguardDone(" + authenticated +")");
+ userActivity();
EventLog.writeEvent(70000, 2);
Message msg = mHandler.obtainMessage(KEYGUARD_DONE, authenticated ? 1 : 0);
mHandler.sendMessage(msg);
+ Trace.endSection();
}
/**
@@ -1384,7 +1428,9 @@
handleReset();
break;
case VERIFY_UNLOCK:
+ Trace.beginSection("KeyguardViewMediator#handleMessage VERIFY_UNLOCK");
handleVerifyUnlock();
+ Trace.endSection();
break;
case NOTIFY_STARTED_GOING_TO_SLEEP:
handleNotifyStartedGoingToSleep();
@@ -1393,25 +1439,37 @@
handleNotifyFinishedGoingToSleep();
break;
case NOTIFY_SCREEN_TURNING_ON:
+ Trace.beginSection("KeyguardViewMediator#handleMessage NOTIFY_SCREEN_TURNING_ON");
handleNotifyScreenTurningOn((IKeyguardDrawnCallback) msg.obj);
+ Trace.endSection();
break;
case NOTIFY_SCREEN_TURNED_ON:
+ Trace.beginSection("KeyguardViewMediator#handleMessage NOTIFY_SCREEN_TURNED_ON");
handleNotifyScreenTurnedOn();
+ Trace.endSection();
break;
case NOTIFY_SCREEN_TURNED_OFF:
handleNotifyScreenTurnedOff();
break;
case NOTIFY_STARTED_WAKING_UP:
+ Trace.beginSection("KeyguardViewMediator#handleMessage NOTIFY_STARTED_WAKING_UP");
handleNotifyStartedWakingUp();
+ Trace.endSection();
break;
case KEYGUARD_DONE:
+ Trace.beginSection("KeyguardViewMediator#handleMessage KEYGUARD_DONE");
handleKeyguardDone(msg.arg1 != 0);
+ Trace.endSection();
break;
case KEYGUARD_DONE_DRAWING:
+ Trace.beginSection("KeyguardViewMediator#handleMessage KEYGUARD_DONE_DRAWING");
handleKeyguardDoneDrawing();
+ Trace.endSection();
break;
case SET_OCCLUDED:
+ Trace.beginSection("KeyguardViewMediator#handleMessage SET_OCCLUDED");
handleSetOccluded(msg.arg1 != 0);
+ Trace.endSection();
break;
case KEYGUARD_TIMEOUT:
synchronized (KeyguardViewMediator.this) {
@@ -1419,15 +1477,19 @@
}
break;
case DISMISS:
- handleDismiss();
+ handleDismiss(msg.arg1 == 1 ? true : false /* allowWhileOccluded */);
break;
case START_KEYGUARD_EXIT_ANIM:
+ Trace.beginSection("KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM");
StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj;
handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration);
FalsingManager.getInstance(mContext).onSucccessfulUnlock();
+ Trace.endSection();
break;
case KEYGUARD_DONE_PENDING_TIMEOUT:
+ Trace.beginSection("KeyguardViewMediator#handleMessage KEYGUARD_DONE_PENDING_TIMEOUT");
Log.w(TAG, "Timeout while waiting for activity drawn!");
+ Trace.endSection();
// Fall through.
case ON_ACTIVITY_DRAWN:
handleOnActivityDrawn();
@@ -1441,6 +1503,7 @@
* @see #KEYGUARD_DONE
*/
private void handleKeyguardDone(boolean authenticated) {
+ Trace.beginSection("KeyguardViewMediator#handleKeyguardDone");
final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
if (mLockPatternUtils.isSecure(currentUser)) {
mLockPatternUtils.getDevicePolicyManager().reportKeyguardDismissed(currentUser);
@@ -1478,6 +1541,7 @@
}
handleHide();
+ Trace.endSection();
}
private void sendUserPresentBroadcast() {
@@ -1502,6 +1566,7 @@
* @see #KEYGUARD_DONE_DRAWING
*/
private void handleKeyguardDoneDrawing() {
+ Trace.beginSection("KeyguardViewMediator#handleKeyguardDoneDrawing");
synchronized(this) {
if (DEBUG) Log.d(TAG, "handleKeyguardDoneDrawing");
if (mWaitingUntilKeyguardVisible) {
@@ -1515,6 +1580,7 @@
mHandler.removeMessages(KEYGUARD_DONE_DRAWING);
}
}
+ Trace.endSection();
}
private void playSounds(boolean locked) {
@@ -1546,10 +1612,12 @@
}
private void updateActivityLockScreenState() {
+ Trace.beginSection("KeyguardViewMediator#updateActivityLockScreenState");
try {
ActivityManagerNative.getDefault().setLockScreenShown(mShowing, mOccluded);
} catch (RemoteException e) {
}
+ Trace.endSection();
}
/**
@@ -1557,6 +1625,7 @@
* @see #SHOW
*/
private void handleShow(Bundle options) {
+ Trace.beginSection("KeyguardViewMediator#handleShow");
final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
if (mLockPatternUtils.isSecure(currentUser)) {
mLockPatternUtils.getDevicePolicyManager().reportKeyguardSecured(currentUser);
@@ -1582,11 +1651,14 @@
mShowKeyguardWakeLock.release();
}
mKeyguardDisplayManager.show();
+ Trace.endSection();
}
private final Runnable mKeyguardGoingAwayRunnable = new Runnable() {
@Override
public void run() {
+ Trace.beginSection("KeyguardViewMediator.mKeyGuardGoingAwayRunnable");
+ if (DEBUG) Log.d(TAG, "keyguardGoingAway");
try {
mStatusBarKeyguardViewManager.keyguardGoingAway();
@@ -1609,6 +1681,7 @@
} catch (RemoteException e) {
Log.e(TAG, "Error while calling WindowManager", e);
}
+ Trace.endSection();
}
};
@@ -1617,11 +1690,11 @@
* @see #HIDE
*/
private void handleHide() {
+ Trace.beginSection("KeyguardViewMediator#handleHide");
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleHide");
- if (UserManager.isSplitSystemUser()
- && KeyguardUpdateMonitor.getCurrentUser() == UserHandle.USER_SYSTEM) {
+ if (mustNotUnlockCurrentUser()) {
// In split system user mode, we never unlock system user. The end user has to
// switch to another user.
// TODO: We should stop it early by disabling the swipe up flow. Right now swipe up
@@ -1645,6 +1718,7 @@
mHideAnimation.getDuration());
}
}
+ Trace.endSection();
}
private void handleOnActivityDrawn() {
@@ -1655,6 +1729,9 @@
}
private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+ Trace.beginSection("KeyguardViewMediator#handleStartKeyguardExitAnimation");
+ if (DEBUG) Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime
+ + " fadeoutDuration=" + fadeoutDuration);
synchronized (KeyguardViewMediator.this) {
if (!mHiding) {
@@ -1669,6 +1746,7 @@
// this to our ViewRootImpl.
mStatusBarKeyguardViewManager.getViewRootImpl().setReportNextDraw();
notifyDrawn(mDrawnCallback);
+ mDrawnCallback = null;
}
// only play "unlock" noises if not on a call (since the incall UI
@@ -1677,6 +1755,7 @@
playSounds(false);
}
+ mWakeAndUnlocking = false;
setShowingLocked(false);
mStatusBarKeyguardViewManager.hide(startTime, fadeoutDuration);
resetKeyguardDonePendingLocked();
@@ -1685,6 +1764,7 @@
adjustStatusBarLocked();
sendUserPresentBroadcast();
}
+ Trace.endSection();
}
private void adjustStatusBarLocked() {
@@ -1736,12 +1816,14 @@
* @see #VERIFY_UNLOCK
*/
private void handleVerifyUnlock() {
+ Trace.beginSection("KeyguardViewMediator#handleVerifyUnlock");
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleVerifyUnlock");
setShowingLocked(true);
mStatusBarKeyguardViewManager.verifyUnlock();
updateActivityLockScreenState();
}
+ Trace.endSection();
}
private void handleNotifyStartedGoingToSleep() {
@@ -1763,13 +1845,16 @@
}
private void handleNotifyStartedWakingUp() {
+ Trace.beginSection("KeyguardViewMediator#handleMotifyStartedWakingUp");
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleNotifyWakingUp");
mStatusBarKeyguardViewManager.onStartedWakingUp();
}
+ Trace.endSection();
}
private void handleNotifyScreenTurningOn(IKeyguardDrawnCallback callback) {
+ Trace.beginSection("KeyguardViewMediator#handleNotifyScreenTurningOn");
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleNotifyScreenTurningOn");
mStatusBarKeyguardViewManager.onScreenTurningOn();
@@ -1781,29 +1866,35 @@
}
}
}
+ Trace.endSection();
}
private void handleNotifyScreenTurnedOn() {
+ Trace.beginSection("KeyguardViewMediator#handleNotifyScreenTurnedOn");
synchronized (this) {
if (DEBUG) Log.d(TAG, "handleNotifyScreenTurnedOn");
mStatusBarKeyguardViewManager.onScreenTurnedOn();
}
+ Trace.endSection();
}
private void handleNotifyScreenTurnedOff() {
synchronized (this) {
if (DEBUG) Log.d(TAG, "handleNotifyScreenTurnedOff");
mStatusBarKeyguardViewManager.onScreenTurnedOff();
+ mDrawnCallback = null;
mWakeAndUnlocking = false;
}
}
private void notifyDrawn(final IKeyguardDrawnCallback callback) {
+ Trace.beginSection("KeyguardViewMediator#notifyDrawn");
try {
callback.onDrawn();
} catch (RemoteException e) {
Slog.w(TAG, "Exception calling onDrawn():", e);
}
+ Trace.endSection();
}
private void resetKeyguardDonePendingLocked() {
@@ -1823,8 +1914,10 @@
}
public void onWakeAndUnlocking() {
+ Trace.beginSection("KeyguardViewMediator#onWakeAndUnlocking");
mWakeAndUnlocking = true;
keyguardDone(true /* authenticated */);
+ Trace.endSection();
}
public StatusBarKeyguardViewManager registerStatusBar(PhoneStatusBar phoneStatusBar,
@@ -1837,9 +1930,11 @@
}
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+ Trace.beginSection("KeyguardViewMediator#startKeyguardExitAnimation");
Message msg = mHandler.obtainMessage(START_KEYGUARD_EXIT_ANIM,
new StartKeyguardExitAnimParams(startTime, fadeoutDuration));
mHandler.sendMessage(msg);
+ Trace.endSection();
}
public void onActivityDrawn() {
@@ -1908,6 +2003,35 @@
}
}
+ private void notifyTrustedChangedLocked(boolean trusted) {
+ int size = mKeyguardStateCallbacks.size();
+ for (int i = size - 1; i >= 0; i--) {
+ try {
+ mKeyguardStateCallbacks.get(i).onTrustedChanged(trusted);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call notifyTrustedChangedLocked", e);
+ if (e instanceof DeadObjectException) {
+ mKeyguardStateCallbacks.remove(i);
+ }
+ }
+ }
+ }
+
+ private void notifyHasLockscreenWallpaperChanged(boolean hasLockscreenWallpaper) {
+ int size = mKeyguardStateCallbacks.size();
+ for (int i = size - 1; i >= 0; i--) {
+ try {
+ mKeyguardStateCallbacks.get(i).onHasLockscreenWallpaperChanged(
+ hasLockscreenWallpaper);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call onHasLockscreenWallpaperChanged", e);
+ if (e instanceof DeadObjectException) {
+ mKeyguardStateCallbacks.remove(i);
+ }
+ }
+ }
+ }
+
public void addStateMonitorCallback(IKeyguardStateCallback callback) {
synchronized (this) {
mKeyguardStateCallbacks.add(callback);
@@ -1915,8 +2039,11 @@
callback.onSimSecureStateChanged(mUpdateMonitor.isSimPinSecure());
callback.onShowingStateChanged(mShowing);
callback.onInputRestrictedStateChanged(mInputRestricted);
+ callback.onTrustedChanged(mUpdateMonitor.getUserHasTrust(
+ KeyguardUpdateMonitor.getCurrentUser()));
+ callback.onHasLockscreenWallpaperChanged(mUpdateMonitor.hasLockscreenWallpaper());
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to call onShowingStateChanged or onSimSecureStateChanged or onInputRestrictedStateChanged", e);
+ Slog.w(TAG, "Failed to call to IKeyguardStateCallback", e);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java b/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java
index aff5d2b..5f23a40 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java
@@ -24,6 +24,7 @@
import android.util.AttributeSet;
import android.view.View;
+import com.android.settingslib.Utils;
import com.android.systemui.R;
public class DataUsageGraph extends View {
@@ -45,7 +46,7 @@
super(context, attrs);
final Resources res = context.getResources();
mTrackColor = context.getColor(R.color.data_usage_graph_track);
- mUsageColor = context.getColor(R.color.system_accent_color);
+ mUsageColor = Utils.getColorAccent(context);
mOverlimitColor = context.getColor(R.color.system_warning_color);
mWarningColor = context.getColor(R.color.data_usage_graph_warning);
mMarkerWidth = res.getDimensionPixelSize(R.dimen.data_usage_graph_marker_width);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 71bd798..afedbe3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -10,6 +10,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+
import com.android.systemui.R;
import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.QSPanel.TileRecord;
@@ -207,6 +208,7 @@
}
if (DEBUG) Log.d(TAG, "Size: " + mNumPages);
mPageIndicator.setNumPages(mNumPages);
+ mDecorGroup.setVisibility(mNumPages > 1 ? View.VISIBLE : View.GONE);
setAdapter(mAdapter);
mAdapter.notifyDataSetChanged();
setCurrentItem(0, false);
@@ -238,7 +240,8 @@
maxHeight = height;
}
}
- setMeasuredDimension(getMeasuredWidth(), maxHeight + mDecorGroup.getMeasuredHeight());
+ setMeasuredDimension(getMeasuredWidth(), maxHeight
+ + (mDecorGroup.getVisibility() != View.GONE ? mDecorGroup.getMeasuredHeight() : 0));
}
private final Runnable mDistribute = new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 2dcb5f4..a21408d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -41,8 +41,7 @@
private static final String ALLOW_FANCY_ANIMATION = "sysui_qs_fancy_anim";
private static final String MOVE_FULL_ROWS = "sysui_qs_move_whole_rows";
- public static final float EXPANDED_TILE_DELAY = .7f;
- private static final float LAST_ROW_EXPANDED_DELAY = .86f;
+ public static final float EXPANDED_TILE_DELAY = .86f;
private final ArrayList<View> mAllViews = new ArrayList<>();
private final ArrayList<View> mTopFiveQs = new ArrayList<>();
@@ -58,7 +57,7 @@
private TouchAnimator mTranslationXAnimator;
private TouchAnimator mTranslationYAnimator;
private TouchAnimator mNonfirstPageAnimator;
- private TouchAnimator mLastRowAnimator;
+ private TouchAnimator mBrightnessAnimator;
private boolean mOnKeyguard;
@@ -144,7 +143,6 @@
TouchAnimator.Builder firstPageBuilder = new Builder();
TouchAnimator.Builder translationXBuilder = new Builder();
TouchAnimator.Builder translationYBuilder = new Builder();
- TouchAnimator.Builder lastRowBuilder = new Builder();
if (mQsPanel.getHost() == null) return;
Collection<QSTile<?>> tiles = mQsPanel.getHost().getTiles();
@@ -152,7 +150,6 @@
int[] loc1 = new int[2];
int[] loc2 = new int[2];
int lastXDiff = 0;
- int lastYDiff = 0;
int lastX = 0;
clearAnimationState();
@@ -175,7 +172,6 @@
final int xDiff = loc2[0] - loc1[0];
final int yDiff = loc2[1] - loc1[1];
lastXDiff = loc1[0] - lastX;
- lastYDiff = yDiff;
// Move the quick tile right from its location to the new one.
translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff);
translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
@@ -209,27 +205,42 @@
mAllViews.add(tileIcon);
} else {
- lastRowBuilder.addFloat(tileView, "alpha", 0, 1);
+ firstPageBuilder.addFloat(tileView, "alpha", 0, 1);
}
mAllViews.add(tileView);
mAllViews.add(label);
count++;
}
if (mAllowFancy) {
+ // Make brightness appear static position and alpha in through second half.
+ View brightness = mQsPanel.getBrightnessView();
+ if (brightness != null) {
+ firstPageBuilder.addFloat(brightness, "translationY", mQsPanel.getHeight(), 0);
+ mBrightnessAnimator = new TouchAnimator.Builder()
+ .addFloat(brightness, "alpha", 0, 1)
+ .setStartDelay(.5f)
+ .build();
+ mAllViews.add(brightness);
+ } else {
+ mBrightnessAnimator = null;
+ }
mFirstPageAnimator = firstPageBuilder
.setListener(this)
.build();
// Fade in the tiles/labels as we reach the final position.
mFirstPageDelayedAnimator = new TouchAnimator.Builder()
.setStartDelay(EXPANDED_TILE_DELAY)
- .addFloat(mQsPanel.getTileLayout(), "alpha", 0, 1).build();
- mLastRowAnimator = lastRowBuilder
- .setStartDelay(LAST_ROW_EXPANDED_DELAY)
- .build();
- Path path = new Path();
- path.moveTo(0, 0);
- path.cubicTo(0, 0, 0, 1, 1, 1);
- PathInterpolatorBuilder interpolatorBuilder = new PathInterpolatorBuilder(0, 0, 0, 1);
+ .addFloat(mQsPanel.getTileLayout(), "alpha", 0, 1)
+ .addFloat(mQsPanel.getFooter().getView(), "alpha", 0, 1).build();
+ mAllViews.add(mQsPanel.getFooter().getView());
+ float px = 0;
+ float py = 1;
+ if (tiles.size() <= 3) {
+ px = 1;
+ } else if (tiles.size() <= 6) {
+ px = .4f;
+ }
+ PathInterpolatorBuilder interpolatorBuilder = new PathInterpolatorBuilder(0, 0, px, py);
translationXBuilder.setInterpolator(interpolatorBuilder.getXInterpolator());
translationYBuilder.setInterpolator(interpolatorBuilder.getYInterpolator());
mTranslationXAnimator = translationXBuilder.build();
@@ -279,7 +290,9 @@
mFirstPageDelayedAnimator.setPosition(position);
mTranslationXAnimator.setPosition(position);
mTranslationYAnimator.setPosition(position);
- mLastRowAnimator.setPosition(position);
+ if (mBrightnessAnimator != null) {
+ mBrightnessAnimator.setPosition(position);
+ }
} else {
mNonfirstPageAnimator.setPosition(position);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
index d5fb8f2..19a5d52 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
@@ -146,7 +146,9 @@
return getHeight();
}
if (mQSDetail.isClosingDetail()) {
- return mQSPanel.getGridHeight() + mHeader.getCollapsedHeight() + getPaddingBottom();
+ int panelHeight = ((LayoutParams) mQSPanel.getLayoutParams()).topMargin
+ + mQSPanel.getMeasuredHeight();
+ return panelHeight + getPaddingBottom();
} else {
return getMeasuredHeight();
}
@@ -235,6 +237,10 @@
mQSPanel.setListening(mListening && mQsExpanded);
}
+ public void setHeaderListening(boolean listening) {
+ mHeader.setListening(listening);
+ }
+
public void setQsExpansion(float expansion, float headerTranslation) {
if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + headerTranslation);
mQsExpansion = expansion;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index a40e5b7..662b6a7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -63,11 +63,12 @@
private boolean mScanState;
private boolean mClosingDetail;
private boolean mFullyExpanded;
- private View mQsDetailHeaderBack;
private BaseStatusBarHeader mHeader;
private boolean mTriggeredExpand;
private int mOpenX;
private int mOpenY;
+ private boolean mAnimatingOpen;
+ private boolean mSwitchState;
public QSDetail(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
@@ -92,7 +93,6 @@
mDetailDoneButton = (TextView) findViewById(android.R.id.button1);
mQsDetailHeader = findViewById(R.id.qs_detail_header);
- mQsDetailHeaderBack = mQsDetailHeader.findViewById(com.android.internal.R.id.up);
mQsDetailHeaderTitle = (TextView) mQsDetailHeader.findViewById(android.R.id.title);
mQsDetailHeaderSwitch = (Switch) mQsDetailHeader.findViewById(android.R.id.toggle);
mQsDetailHeaderProgress = (ImageView) findViewById(R.id.qs_detail_header_progress);
@@ -109,7 +109,6 @@
mQsPanel.closeDetail();
}
};
- mQsDetailHeaderBack.setOnClickListener(doneListener);
mDetailDoneButton.setOnClickListener(doneListener);
}
@@ -161,7 +160,7 @@
mQsDetailHeader.setClickable(false);
} else {
mQsDetailHeaderSwitch.setVisibility(VISIBLE);
- mQsDetailHeaderSwitch.setChecked(toggleState);
+ handleToggleStateChanged(toggleState, adapter.getToggleEnabled());
mQsDetailHeader.setClickable(true);
mQsDetailHeader.setOnClickListener(new OnClickListener() {
@Override
@@ -231,6 +230,7 @@
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
if (visibleDiff) {
+ mAnimatingOpen = adapter != null;
if (mFullyExpanded || mDetailAdapter != null) {
setAlpha(1);
mClipper.animateCircularClip(x, y, mDetailAdapter != null, listener);
@@ -243,8 +243,14 @@
}
}
- private void handleToggleStateChanged(boolean state) {
+ private void handleToggleStateChanged(boolean state, boolean toggleEnabled) {
+ mSwitchState = state;
+ if (mAnimatingOpen) {
+ return;
+ }
mQsDetailHeaderSwitch.setChecked(state);
+ mQsDetailHeader.setEnabled(toggleEnabled);
+ mQsDetailHeaderSwitch.setEnabled(toggleEnabled);
}
private void handleScanStateChanged(boolean state) {
@@ -260,13 +266,19 @@
}
}
+ private void checkPendingAnimations() {
+ handleToggleStateChanged(mSwitchState,
+ mDetailAdapter != null && mDetailAdapter.getToggleEnabled());
+ }
+
private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
@Override
public void onToggleStateChanged(final boolean state) {
post(new Runnable() {
@Override
public void run() {
- handleToggleStateChanged(state);
+ handleToggleStateChanged(state,
+ mDetailAdapter != null && mDetailAdapter.getToggleEnabled());
}
});
}
@@ -297,6 +309,8 @@
// If we have been cancelled, remove the listener so that onAnimationEnd doesn't get
// called, this will avoid accidentally turning off the grid when we don't want to.
animation.removeListener(this);
+ mAnimatingOpen = false;
+ checkPendingAnimations();
};
@Override
@@ -306,6 +320,8 @@
mQsPanel.setGridContentVisibility(false);
mHeader.setVisibility(View.INVISIBLE);
}
+ mAnimatingOpen = false;
+ checkPendingAnimations();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index 932b4f5..15ae4ad 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -57,6 +58,7 @@
private boolean mIsVisible;
private boolean mIsIconVisible;
private int mFooterTextId;
+ private int mFooterIconId;
public QSFooter(QSPanel qsPanel, Context context) {
mRootView = LayoutInflater.from(context)
@@ -64,6 +66,7 @@
mRootView.setOnClickListener(this);
mFooterText = (TextView) mRootView.findViewById(R.id.footer_text);
mFooterIcon = (ImageView) mRootView.findViewById(R.id.footer_icon);
+ mFooterIconId = R.drawable.ic_qs_vpn;
mContext = context;
mMainHandler = new Handler();
}
@@ -118,6 +121,14 @@
mIsVisible = true;
} else {
mFooterTextId = R.string.vpn_footer;
+ // Update the VPN footer icon, if needed.
+ int footerIconId = (mSecurityController.isVpnBranded()
+ ? R.drawable.ic_qs_branded_vpn
+ : R.drawable.ic_qs_vpn);
+ if (mFooterIconId != footerIconId) {
+ mFooterIconId = footerIconId;
+ mMainHandler.post(mUpdateIcon);
+ }
mIsVisible = mIsIconVisible;
}
mMainHandler.post(mUpdateDisplayState);
@@ -196,6 +207,13 @@
}
}
+ private final Runnable mUpdateIcon = new Runnable() {
+ @Override
+ public void run() {
+ mFooterIcon.setImageResource(mFooterIconId);
+ }
+ };
+
private final Runnable mUpdateDisplayState = new Runnable() {
@Override
public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 636a9a50..fef8930 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -23,7 +23,6 @@
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
@@ -106,8 +105,6 @@
R.layout.qs_paged_tile_layout, this, false);
mTileLayout.setListening(mListening);
addView((View) mTileLayout);
- findViewById(android.R.id.edit).setOnClickListener(view ->
- mHost.startRunnableDismissingKeyguard(() -> showEdit(view)));
}
public boolean isShowingCustomize() {
@@ -168,6 +165,10 @@
brightnessSlider.setMirrorController(c);
}
+ View getBrightnessView() {
+ return mBrightnessView;
+ }
+
public void setCallback(Callback callback) {
mCallback = callback;
}
@@ -181,6 +182,7 @@
if (mCustomizePanel != null) {
mCustomizePanel.setHost(mHost);
}
+ mBrightnessController.setBackgroundLooper(host.getLooper());
}
public QSTileHost getHost() {
@@ -244,10 +246,12 @@
if (mListening) {
refreshAllTiles();
}
- if (listening) {
- mBrightnessController.registerCallbacks();
- } else {
- mBrightnessController.unregisterCallbacks();
+ if (mBrightnessView.getVisibility() == View.VISIBLE) {
+ if (listening) {
+ mBrightnessController.registerCallbacks();
+ } else {
+ mBrightnessController.unregisterCallbacks();
+ }
}
}
@@ -369,7 +373,7 @@
}
- private void showEdit(final View v) {
+ public void showEdit(final View v) {
v.post(new Runnable() {
@Override
public void run() {
@@ -377,8 +381,8 @@
if (!mCustomizePanel.isCustomizing()) {
int[] loc = new int[2];
v.getLocationInWindow(loc);
- int x = loc[0];
- int y = loc[1];
+ int x = loc[0] + v.getWidth() / 2;
+ int y = loc[1] + v.getHeight() / 2;
mCustomizePanel.show(x, y);
}
}
@@ -503,6 +507,10 @@
return null;
}
+ public QSFooter getFooter() {
+ return mFooter;
+ }
+
private class H extends Handler {
private static final int SHOW_DETAIL = 1;
private static final int SET_TILE_VISIBILITY = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 27b079a..2fda6ea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -39,7 +39,6 @@
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.NightModeController;
import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -149,6 +148,9 @@
public interface DetailAdapter {
CharSequence getTitle();
Boolean getToggleState();
+ default boolean getToggleEnabled() {
+ return true;
+ }
View createDetailView(Context context, View convertView, ViewGroup parent);
Intent getSettingsIntent();
void setToggleState(boolean state);
@@ -447,7 +449,6 @@
UserInfoController getUserInfoController();
BatteryController getBatteryController();
TileServices getTileServices();
- NightModeController getNightModeController();
void removeTile(String tileSpec);
ManagedProfileController getManagedProfileController();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
index feacaa0..5ed19ef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
@@ -63,6 +63,7 @@
setClipChildren(false);
setClipToPadding(false);
mCollapsedView = collapsedView;
+ setFocusable(true);
}
private Drawable newTileBackground() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index f287f1b..f09275d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -146,12 +146,11 @@
};
public int getNumQuickTiles(Context context) {
- return TunerService.get(context).getValue(NUM_QUICK_TILES, 5);
+ return TunerService.get(context).getValue(NUM_QUICK_TILES, 6);
}
private static class HeaderTileLayout extends LinearLayout implements QSTileLayout {
- private final Space mEndSpacer;
protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
private boolean mListening;
@@ -161,25 +160,6 @@
setClipToPadding(false);
setGravity(Gravity.CENTER_VERTICAL);
setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
-
- mEndSpacer = new Space(context);
- mEndSpacer.setLayoutParams(generateLayoutParams());
- updateDownArrowMargin();
- addView(mEndSpacer);
- setOrientation(LinearLayout.HORIZONTAL);
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- updateDownArrowMargin();
- }
-
- private void updateDownArrowMargin() {
- LayoutParams params = (LayoutParams) mEndSpacer.getLayoutParams();
- params.setMarginStart(mContext.getResources().getDimensionPixelSize(
- R.dimen.qs_expand_margin));
- mEndSpacer.setLayoutParams(params);
}
@Override
@@ -193,11 +173,11 @@
@Override
public void addTile(TileRecord tile) {
- addView(tile.tileView, getChildCount() - 1 /* Leave icon at end */,
- generateLayoutParams());
- // Add a spacer.
- addView(new Space(mContext), getChildCount() - 1 /* Leave icon at end */,
- generateSpaceParams());
+ if (getChildCount() != 0) {
+ // Add a spacer.
+ addView(new Space(mContext), getChildCount(), generateSpaceParams());
+ }
+ addView(tile.tileView, getChildCount(), generateLayoutParams());
mRecords.add(tile);
tile.tile.setListening(this, mListening);
}
@@ -222,8 +202,10 @@
int childIndex = getChildIndex(tile.tileView);
// Remove the tile.
removeViewAt(childIndex);
- // Remove its spacer as well.
- removeViewAt(childIndex);
+ if (getChildCount() != 0) {
+ // Remove its spacer as well.
+ removeViewAt(childIndex);
+ }
mRecords.remove(tile);
tile.tile.setListening(this, false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 8e4ed91..8d7f6ee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -19,6 +19,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.ColorDrawable;
import android.os.Handler;
@@ -71,6 +72,7 @@
private final Handler mHandler = new Handler();
private final List<TileInfo> mTiles = new ArrayList<>();
private final ItemTouchHelper mItemTouchHelper;
+ private final ItemDecoration mDecoration;
private final AccessibilityManager mAccessibilityManager;
private int mEditIndex;
private int mTileDividerIndex;
@@ -88,6 +90,7 @@
mContext = context;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mItemTouchHelper = new ItemTouchHelper(mCallbacks);
+ mDecoration = new TileItemDecoration(context);
}
public void setHost(QSTileHost host) {
@@ -294,6 +297,9 @@
mAccessibilityMoving = false;
mTiles.remove(mEditIndex--);
notifyItemRemoved(mEditIndex - 1);
+ // Don't remove items when the last position is selected.
+ if (position == mEditIndex) position--;
+
move(mAccessibilityFromIndex, position, v);
notifyDataSetChanged();
}
@@ -459,9 +465,16 @@
}
};
- private final ItemDecoration mDecoration = new ItemDecoration() {
- // TODO: Move this to resource.
- private final ColorDrawable mDrawable = new ColorDrawable(0xff384248);
+ private class TileItemDecoration extends ItemDecoration {
+ private final ColorDrawable mDrawable;
+
+ private TileItemDecoration(Context context) {
+ TypedArray ta =
+ context.obtainStyledAttributes(new int[]{android.R.attr.colorSecondary});
+ mDrawable = new ColorDrawable(ta.getColor(0, 0));
+ ta.recycle();
+ }
+
@Override
public void onDraw(Canvas c, RecyclerView parent, State state) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 1431b22..40ef6eb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -57,8 +57,7 @@
}
private void addSystemTiles(final QSTileHost host) {
- String possible = mContext.getString(R.string.quick_settings_tiles_default)
- + ",hotspot,inversion,saver,work,cast,night";
+ String possible = mContext.getString(R.string.quick_settings_tiles_stock);
String[] possibleTiles = possible.split(",");
final Handler qsHandler = new Handler(host.getLooper());
final Handler mainHandler = new Handler(Looper.getMainLooper());
@@ -145,9 +144,16 @@
PackageManager pm = mContext.getPackageManager();
List<ResolveInfo> services = pm.queryIntentServicesAsUser(
new Intent(TileService.ACTION_QS_TILE), 0, ActivityManager.getCurrentUser());
+ String stockTiles = mContext.getString(R.string.quick_settings_tiles_stock);
for (ResolveInfo info : services) {
String packageName = info.serviceInfo.packageName;
ComponentName componentName = new ComponentName(packageName, info.serviceInfo.name);
+
+ // Don't include apps that are a part of the default tile set.
+ if (stockTiles.contains(componentName.flattenToString())) {
+ continue;
+ }
+
final CharSequence appLabel = info.serviceInfo.applicationInfo.loadLabel(pm);
String spec = CustomTile.toSpec(componentName);
State state = getState(params[0], spec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index d3f5d26..b36221d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -71,7 +71,7 @@
super(host);
mWindowManager = WindowManagerGlobal.getWindowManagerService();
mComponent = ComponentName.unflattenFromString(action);
- mTile = new Tile(mComponent);
+ mTile = new Tile();
setTileIcon();
mServiceManager = host.getTileServices().getTileWrapper(this);
mService = mServiceManager.getTileService();
@@ -82,8 +82,11 @@
private void setTileIcon() {
try {
PackageManager pm = mContext.getPackageManager();
- ServiceInfo info = pm.getServiceInfo(mComponent,
- PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE);
+ int flags = PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE;
+ if (isSystemApp(pm)) {
+ flags |= PackageManager.MATCH_DISABLED_COMPONENTS;
+ }
+ ServiceInfo info = pm.getServiceInfo(mComponent, flags);
int icon = info.icon != 0 ? info.icon
: info.applicationInfo.icon;
// Update the icon if its not set or is the default icon.
@@ -103,6 +106,10 @@
}
}
+ private boolean isSystemApp(PackageManager pm) throws PackageManager.NameNotFoundException {
+ return pm.getApplicationInfo(mComponent.getPackageName(), 0).isSystemApp();
+ }
+
/**
* Compare two icons, only works for resources.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java b/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java
index 407453c..451e1f6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java
@@ -94,4 +94,8 @@
return false;
}
}
+
+ public IQSTileService getService() {
+ return mService;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index 79f9de6..28da69c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -26,6 +26,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
import android.net.Uri;
+import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -37,6 +38,7 @@
import android.support.annotation.VisibleForTesting;
import android.util.ArraySet;
import android.util.Log;
+
import libcore.util.Objects;
import java.util.Set;
@@ -63,10 +65,14 @@
private static final int MAX_BIND_RETRIES = 5;
private static final int BIND_RETRY_DELAY = 1000;
+ // Shared prefs that hold tile lifecycle info.
+ private static final String TILES = "tiles_prefs";
+
private final Context mContext;
private final Handler mHandler;
private final Intent mIntent;
private final UserHandle mUser;
+ private final IBinder mToken = new Binder();
private Set<Integer> mQueuedMessages = new ArraySet<>();
private QSTileServiceWrapper mWrapper;
@@ -88,7 +94,7 @@
mHandler = handler;
mIntent = intent;
mIntent.putExtra(TileService.EXTRA_SERVICE, service.asBinder());
- mIntent.putExtra(TileService.EXTRA_COMPONENT, intent.getComponent());
+ mIntent.putExtra(TileService.EXTRA_TOKEN, mToken);
mUser = user;
if (DEBUG) Log.d(TAG, "Creating " + mIntent + " " + mUser);
}
@@ -396,7 +402,20 @@
handleDeath();
}
+ public IBinder getToken() {
+ return mToken;
+ }
+
public interface TileChangeListener {
void onTileChanged(ComponentName tile);
}
+
+ public static boolean isTileAdded(Context context, ComponentName component) {
+ return context.getSharedPreferences(TILES, 0).getBoolean(component.flattenToString(), false);
+ }
+
+ public static void setTileAdded(Context context, ComponentName component, boolean added) {
+ context.getSharedPreferences(TILES, 0).edit().putBoolean(component.flattenToString(),
+ added).commit();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index 3d030f9..8493cc6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -25,6 +25,7 @@
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Handler;
+import android.os.IBinder;
import android.os.UserHandle;
import android.service.quicksettings.IQSTileService;
import android.service.quicksettings.Tile;
@@ -32,6 +33,7 @@
import android.support.annotation.VisibleForTesting;
import android.util.Log;
+import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener;
import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener;
import java.util.List;
@@ -86,8 +88,14 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
- mServices.getContext().registerReceiverAsUser(mUninstallReceiver,
+ Context context = mServices.getContext();
+ context.registerReceiverAsUser(mUninstallReceiver,
new UserHandle(ActivityManager.getCurrentUser()), filter, null, mHandler);
+ ComponentName component = tileLifecycleManager.getComponent();
+ if (!TileLifecycleManager.isTileAdded(context, component)) {
+ TileLifecycleManager.setTileAdded(context, component, true);
+ mStateManager.onTileAdded();
+ }
}
public void setTileChangeListener(TileChangeListener changeListener) {
@@ -106,6 +114,10 @@
return mStateManager;
}
+ public IBinder getToken() {
+ return mStateManager.getToken();
+ }
+
public void setBindRequested(boolean bindRequested) {
if (mBindRequested == bindRequested) return;
mBindRequested = bindRequested;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 6f0bed2..6bc94b2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -25,10 +25,12 @@
import android.graphics.drawable.Icon;
import android.os.Binder;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.quicksettings.IQSService;
+import android.service.quicksettings.IQSTileService;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
import android.util.ArrayMap;
@@ -52,6 +54,7 @@
private final ArrayMap<CustomTile, TileServiceManager> mServices = new ArrayMap<>();
private final ArrayMap<ComponentName, CustomTile> mTiles = new ArrayMap<>();
+ private final ArrayMap<IBinder, CustomTile> mTokenMap = new ArrayMap<>();
private final Context mContext;
private final Handler mHandler;
private final Handler mMainHandler;
@@ -82,6 +85,7 @@
synchronized (mServices) {
mServices.put(tile, service);
mTiles.put(component, tile);
+ mTokenMap.put(service.getToken(), tile);
}
return service;
}
@@ -95,6 +99,7 @@
service.setBindAllowed(false);
service.handleDestroy();
mServices.remove(tile);
+ mTokenMap.remove(service.getToken());
mTiles.remove(tile.getComponent());
final String slot = tile.getComponent().getClassName();
mMainHandler.post(new Runnable() {
@@ -138,8 +143,9 @@
}
}
- private void verifyCaller(String packageName) {
+ private void verifyCaller(CustomTile tile) {
try {
+ String packageName = tile.getComponent().getPackageName();
int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
Binder.getCallingUserHandle().getIdentifier());
if (Binder.getCallingUid() != uid) {
@@ -170,11 +176,10 @@
}
@Override
- public void updateQsTile(Tile tile) {
- ComponentName componentName = tile.getComponentName();
- verifyCaller(componentName.getPackageName());
- CustomTile customTile = getTileForComponent(componentName);
+ public void updateQsTile(Tile tile, IBinder token) {
+ CustomTile customTile = getTileForToken(token);
if (customTile != null) {
+ verifyCaller(customTile);
synchronized (mServices) {
final TileServiceManager tileServiceManager = mServices.get(customTile);
tileServiceManager.clearPendingBind();
@@ -186,11 +191,10 @@
}
@Override
- public void onStartSuccessful(Tile tile) {
- ComponentName componentName = tile.getComponentName();
- verifyCaller(componentName.getPackageName());
- CustomTile customTile = getTileForComponent(componentName);
+ public void onStartSuccessful(IBinder token) {
+ CustomTile customTile = getTileForToken(token);
if (customTile != null) {
+ verifyCaller(customTile);
synchronized (mServices) {
final TileServiceManager tileServiceManager = mServices.get(customTile);
tileServiceManager.clearPendingBind();
@@ -200,11 +204,10 @@
}
@Override
- public void onShowDialog(Tile tile) {
- ComponentName componentName = tile.getComponentName();
- verifyCaller(componentName.getPackageName());
- CustomTile customTile = getTileForComponent(componentName);
+ public void onShowDialog(IBinder token) {
+ CustomTile customTile = getTileForToken(token);
if (customTile != null) {
+ verifyCaller(customTile);
customTile.onDialogShown();
mHost.collapsePanels();
mServices.get(customTile).setShowingDialog(true);
@@ -212,34 +215,32 @@
}
@Override
- public void onDialogHidden(Tile tile) {
- ComponentName componentName = tile.getComponentName();
- verifyCaller(componentName.getPackageName());
- CustomTile customTile = getTileForComponent(componentName);
+ public void onDialogHidden(IBinder token) {
+ CustomTile customTile = getTileForToken(token);
if (customTile != null) {
+ verifyCaller(customTile);
mServices.get(customTile).setShowingDialog(false);
customTile.onDialogHidden();
}
}
@Override
- public void onStartActivity(Tile tile) {
- ComponentName componentName = tile.getComponentName();
- verifyCaller(componentName.getPackageName());
- CustomTile customTile = getTileForComponent(componentName);
+ public void onStartActivity(IBinder token) {
+ CustomTile customTile = getTileForToken(token);
if (customTile != null) {
+ verifyCaller(customTile);
mHost.collapsePanels();
}
}
@Override
- public void updateStatusIcon(Tile tile, Icon icon, String contentDescription) {
- final ComponentName componentName = tile.getComponentName();
- String packageName = componentName.getPackageName();
- verifyCaller(packageName);
- CustomTile customTile = getTileForComponent(componentName);
+ public void updateStatusIcon(IBinder token, Icon icon, String contentDescription) {
+ CustomTile customTile = getTileForToken(token);
if (customTile != null) {
+ verifyCaller(customTile);
try {
+ ComponentName componentName = customTile.getComponent();
+ String packageName = componentName.getPackageName();
UserHandle userHandle = getCallingUserHandle();
PackageInfo info = mContext.getPackageManager().getPackageInfoAsUser(packageName, 0,
userHandle.getIdentifier());
@@ -263,21 +264,20 @@
}
@Override
- public Tile getTile(ComponentName componentName) {
- verifyCaller(componentName.getPackageName());
- CustomTile customTile = getTileForComponent(componentName);
+ public Tile getTile(IBinder token) {
+ CustomTile customTile = getTileForToken(token);
if (customTile != null) {
+ verifyCaller(customTile);
return customTile.getQsTile();
}
return null;
}
@Override
- public void startUnlockAndRun(Tile tile) {
- ComponentName componentName = tile.getComponentName();
- verifyCaller(componentName.getPackageName());
- CustomTile customTile = getTileForComponent(componentName);
+ public void startUnlockAndRun(IBinder token) {
+ CustomTile customTile = getTileForToken(token);
if (customTile != null) {
+ verifyCaller(customTile);
customTile.startUnlockAndRun();
}
}
@@ -294,6 +294,12 @@
return keyguardMonitor.isSecure() && keyguardMonitor.isShowing();
}
+ private CustomTile getTileForToken(IBinder token) {
+ synchronized (mServices) {
+ return mTokenMap.get(token);
+ }
+ }
+
private CustomTile getTileForComponent(ComponentName component) {
synchronized (mServices) {
return mTiles.get(component);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 7a23910..f1e8749 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
@@ -90,11 +91,11 @@
mHost.startActivityDismissingKeyguard(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
return;
}
+ showDetail(true);
if (!mState.value) {
mState.value = true;
mController.setBluetoothEnabled(true);
}
- showDetail(true);
}
@Override
@@ -208,6 +209,12 @@
}
@Override
+ public boolean getToggleEnabled() {
+ return mController.getBluetoothState() == BluetoothAdapter.STATE_OFF
+ || mController.getBluetoothState() == BluetoothAdapter.STATE_ON;
+ }
+
+ @Override
public Intent getSettingsIntent() {
return BLUETOOTH_SETTINGS;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java
index bad4e79..a63eabc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles;
+import android.annotation.ColorInt;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -24,6 +25,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.settingslib.Utils;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
@@ -64,7 +66,7 @@
final Resources res = mContext.getResources();
final int titleId;
final long bytes;
- int usageColor = R.color.system_accent_color;
+ @ColorInt int usageColor = 0;
final String top;
String bottom = null;
if (info.usageLevel < info.warningLevel || info.limitLevel <= 0) {
@@ -89,14 +91,18 @@
formatBytes(info.usageLevel));
bottom = res.getString(R.string.quick_settings_cellular_detail_data_limit,
formatBytes(info.limitLevel));
- usageColor = R.color.system_warning_color;
+ usageColor = mContext.getColor(R.color.system_warning_color);
+ }
+
+ if (usageColor == 0) {
+ usageColor = Utils.getColorAccent(mContext);
}
final TextView title = (TextView) findViewById(android.R.id.title);
title.setText(titleId);
final TextView usage = (TextView) findViewById(R.id.usage_text);
usage.setText(formatBytes(bytes));
- usage.setTextColor(mContext.getColor(usageColor));
+ usage.setTextColor(usageColor);
final DataUsageGraph graph = (DataUsageGraph) findViewById(R.id.usage_graph);
graph.setLevels(info.limitLevel, info.warningLevel, info.usageLevel);
final TextView carrier = (TextView) findViewById(R.id.usage_carrier_text);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 04cb553..91821ba 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -122,9 +122,9 @@
if (mState.value) {
mController.setZen(Global.ZEN_MODE_OFF, null, TAG);
} else {
+ showDetail(true);
int zen = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN, Global.ZEN_MODE_ALARMS);
mController.setZen(zen, null, TAG);
- showDetail(true);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
new file mode 100644
index 0000000..9415b27
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import android.app.ActivityManager;
+import android.content.Intent;
+import android.provider.Settings;
+import android.widget.Switch;
+
+import com.android.internal.app.NightDisplayController;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+
+public class NightDisplayTile extends QSTile<QSTile.BooleanState>
+ implements NightDisplayController.Callback {
+
+ private NightDisplayController mController;
+ private boolean mIsListening;
+
+ public NightDisplayTile(Host host) {
+ super(host);
+ mController = new NightDisplayController(mContext, ActivityManager.getCurrentUser());
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return NightDisplayController.isAvailable(mContext);
+ }
+
+ @Override
+ public BooleanState newTileState() {
+ return new BooleanState();
+ }
+
+ @Override
+ protected void handleClick() {
+ final boolean activated = !mState.value;
+ MetricsLogger.action(mContext, getMetricsCategory(), activated);
+ mController.setActivated(activated);
+ }
+
+ @Override
+ protected void handleUserSwitch(int newUserId) {
+ // Stop listening to the old controller.
+ if (mIsListening) {
+ mController.setListener(null);
+ }
+
+ // Make a new controller for the new user.
+ mController = new NightDisplayController(mContext, newUserId);
+ if (mIsListening) {
+ mController.setListener(this);
+ }
+
+ super.handleUserSwitch(newUserId);
+ }
+
+ @Override
+ protected void handleUpdateState(BooleanState state, Object arg) {
+ final boolean isActivated = mController.isActivated();
+ state.value = isActivated;
+ state.label = mContext.getString(R.string.quick_settings_night_display_label);
+ state.icon = ResourceIcon.get(isActivated ? R.drawable.ic_qs_night_display_on
+ : R.drawable.ic_qs_night_display_off);
+ state.contentDescription = mContext.getString(isActivated
+ ? R.string.quick_settings_night_display_summary_on
+ : R.string.quick_settings_night_display_summary_off);
+ state.minimalAccessibilityClassName = state.expandedAccessibilityClassName
+ = Switch.class.getName();
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsEvent.QS_NIGHT_DISPLAY;
+ }
+
+ @Override
+ public Intent getLongClickIntent() {
+ return new Intent(Settings.ACTION_NIGHT_DISPLAY_SETTINGS);
+ }
+
+ @Override
+ protected void setListening(boolean listening) {
+ mIsListening = listening;
+ if (listening) {
+ mController.setListener(this);
+ refreshState();
+ } else {
+ mController.setListener(null);
+ }
+ }
+
+ @Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_night_display_label);
+ }
+
+ @Override
+ public void onActivated(boolean activated) {
+ refreshState();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 661212c..9ce1f31 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -113,11 +113,11 @@
mHost.startActivityDismissingKeyguard(new Intent(Settings.ACTION_WIFI_SETTINGS));
return;
}
+ showDetail(true);
if (!mState.value) {
mController.setWifiEnabled(true);
mState.value = true;
}
- showDetail(true);
}
@Override
@@ -368,5 +368,5 @@
}
mItems.setItems(items);
}
- };
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 0f356e0..7bdb1c4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -801,9 +801,13 @@
Recents.getTaskLoader().dump(prefix, writer);
String id = Integer.toHexString(System.identityHashCode(this));
+ long lastStackActiveTime = Prefs.getLong(this,
+ Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, -1);
writer.print(prefix); writer.print(TAG);
writer.print(" visible="); writer.print(mIsVisible ? "Y" : "N");
+ writer.print(" lastStackTaskActiveTime="); writer.print(lastStackActiveTime);
+ writer.print(" currentTime="); writer.print(System.currentTimeMillis());
writer.print(" [0x"); writer.print(id); writer.print("]");
writer.println();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index 7161053..914035b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -29,6 +29,7 @@
public boolean launchedWithAltTab;
public boolean launchedFromApp;
+ public boolean launchedFromBlacklistedApp;
public boolean launchedFromHome;
public boolean launchedViaDragGesture;
public boolean launchedViaDockGesture;
@@ -39,6 +40,7 @@
public void reset() {
launchedFromHome = false;
launchedFromApp = false;
+ launchedFromBlacklistedApp = false;
launchedToTaskId = -1;
launchedWithAltTab = false;
launchedViaDragGesture = false;
@@ -53,8 +55,14 @@
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
if (launchedFromApp) {
if (!launchState.launchedWithAltTab && debugFlags.isFastToggleRecentsEnabled()) {
- // If fast toggling, focus the front most task so that the next tap will focus the
- // N-1 task
+ // If fast toggling, focus the front most task so that the next tap will launch the
+ // task
+ return numTasks - 1;
+ }
+
+ if (launchState.launchedFromBlacklistedApp) {
+ // If we are launching from a blacklisted app, focus the front most task so that the
+ // next tap will launch the task
return numTasks - 1;
}
@@ -67,7 +75,7 @@
return -1;
}
- // If coming from home, focus the first task
+ // If coming from home, focus the front most task
return numTasks - 1;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 7e1deec..2757fc4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -609,7 +609,7 @@
stackLayout.setSystemInsets(systemInsets);
if (stack != null) {
stackLayout.getTaskStackBounds(displayRect, windowRect, systemInsets.top,
- systemInsets.right, mTaskStackBounds);
+ systemInsets.left, systemInsets.right, mTaskStackBounds);
stackLayout.reset();
stackLayout.initialize(displayRect, windowRect, mTaskStackBounds,
TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack));
@@ -807,15 +807,19 @@
boolean isHomeStackVisible, boolean animate, int growTarget) {
RecentsTaskLoader loader = Recents.getTaskLoader();
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ boolean isBlacklisted = (runningTask != null)
+ ? ssp.isBlackListedActivity(runningTask.baseActivity.getClassName())
+ : false;
- int runningTaskId = !mLaunchedWhileDocking && (runningTask != null)
+ int runningTaskId = !mLaunchedWhileDocking && !isBlacklisted && (runningTask != null)
? runningTask.id
: -1;
// In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
// should always preload the tasks now. If we are dragging in recents, reload them as
// the stacks might have changed.
- if (mLaunchedWhileDocking || mTriggeredFromAltTab ||sInstanceLoadPlan == null) {
+ if (mLaunchedWhileDocking || mTriggeredFromAltTab || sInstanceLoadPlan == null) {
// Create a new load plan if preloadRecents() was never triggered
sInstanceLoadPlan = loader.createLoadPlan(mContext);
}
@@ -825,11 +829,13 @@
TaskStack stack = sInstanceLoadPlan.getTaskStack();
boolean hasRecentTasks = stack.getTaskCount() > 0;
- boolean useThumbnailTransition = (runningTask != null) && !isHomeStackVisible && hasRecentTasks;
+ boolean useThumbnailTransition = (runningTask != null) && !isHomeStackVisible &&
+ hasRecentTasks;
// Update the launch state that we need in updateHeaderBarLayout()
launchState.launchedFromHome = !useThumbnailTransition && !mLaunchedWhileDocking;
launchState.launchedFromApp = useThumbnailTransition || mLaunchedWhileDocking;
+ launchState.launchedFromBlacklistedApp = launchState.launchedFromApp && isBlacklisted;
launchState.launchedViaDockGesture = mLaunchedWhileDocking;
launchState.launchedViaDragGesture = mDraggingInRecents;
launchState.launchedToTaskId = runningTaskId;
@@ -857,7 +863,9 @@
}
ActivityOptions opts;
- if (useThumbnailTransition) {
+ if (isBlacklisted) {
+ opts = getUnknownTransitionActivityOptions();
+ } else if (useThumbnailTransition) {
// Try starting with a thumbnail transition
opts = getThumbnailTransitionActivityOptions(runningTask, mDummyStackView,
windowOverrideRect);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 37a4948..b896f8a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -243,6 +243,9 @@
if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
Collections.addAll(sRecentsBlacklist,
res.getStringArray(R.array.recents_tv_blacklist_array));
+ } else {
+ Collections.addAll(sRecentsBlacklist,
+ res.getStringArray(R.array.recents_blacklist_array));
}
}
@@ -261,6 +264,13 @@
}
/**
+ * @return whether the provided {@param className} is blacklisted
+ */
+ public boolean isBlackListedActivity(String className) {
+ return sRecentsBlacklist.contains(className);
+ }
+
+ /**
* Returns a list of the recents tasks.
*
* @param includeFrontMostExcludedTask if set, will ensure that the front most excluded task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index ca59831..ba31e3e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -200,6 +200,10 @@
if (cachedThumbnailData.thumbnail == null) {
cachedThumbnailData.thumbnail = mDefaultThumbnail;
+ } else {
+ // Kick off an early upload of the bitmap to GL so
+ // that this won't jank the first frame it's drawn in.
+ cachedThumbnailData.thumbnail.prepareToDraw();
}
// When svelte, we trim the memory to just the visible thumbnails when
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 26200d0..745f5a5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -275,10 +275,16 @@
new RectF(0, 0.5f, 1, 1));
@Override
- public boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget) {
- return isCurrentTarget
- ? areaContainsPoint(expandedTouchDockArea, width, height, x, y)
- : areaContainsPoint(touchArea, width, height, x, y);
+ public boolean acceptsDrop(int x, int y, int width, int height, Rect insets,
+ boolean isCurrentTarget) {
+ if (isCurrentTarget) {
+ getMappedRect(expandedTouchDockArea, width, height, mTmpRect);
+ return mTmpRect.contains(x, y);
+ } else {
+ getMappedRect(touchArea, width, height, mTmpRect);
+ updateBoundsWithSystemInsets(mTmpRect, insets);
+ return mTmpRect.contains(x, y);
+ }
}
// Represents the view state of this dock state
@@ -423,6 +429,7 @@
private final RectF touchArea;
private final RectF dockArea;
private final RectF expandedTouchDockArea;
+ private static final Rect mTmpRect = new Rect();
/**
* @param createMode used to pass to ActivityManager to dock the task
@@ -452,23 +459,11 @@
}
/**
- * Returns whether {@param x} and {@param y} are contained in the area scaled to the
- * given {@param width} and {@param height}.
- */
- public boolean areaContainsPoint(RectF area, int width, int height, float x, float y) {
- int left = (int) (area.left * width);
- int top = (int) (area.top * height);
- int right = (int) (area.right * width);
- int bottom = (int) (area.bottom * height);
- return x >= left && y >= top && x <= right && y <= bottom;
- }
-
- /**
* Returns the docked task bounds with the given {@param width} and {@param height}.
*/
- public Rect getPreDockedBounds(int width, int height) {
- return new Rect((int) (dockArea.left * width), (int) (dockArea.top * height),
- (int) (dockArea.right * width), (int) (dockArea.bottom * height));
+ public Rect getPreDockedBounds(int width, int height, Rect insets) {
+ getMappedRect(dockArea, width, height, mTmpRect);
+ return updateBoundsWithSystemInsets(mTmpRect, insets);
}
/**
@@ -511,10 +506,34 @@
int top = dockArea.bottom < 1f
? 0
: insets.top;
- layoutAlgorithm.getTaskStackBounds(displayRect, windowRectOut, top, insets.right,
+ // For now, ignore the left insets since we always dock on the left and show Recents
+ // on the right
+ layoutAlgorithm.getTaskStackBounds(displayRect, windowRectOut, top, 0, insets.right,
taskStackBounds);
return taskStackBounds;
}
+
+ /**
+ * Returns the expanded bounds in certain dock sides such that the bounds account for the
+ * system insets (namely the vertical nav bar). This call modifies and returns the given
+ * {@param bounds}.
+ */
+ private Rect updateBoundsWithSystemInsets(Rect bounds, Rect insets) {
+ if (dockSide == DOCKED_LEFT) {
+ bounds.right += insets.left;
+ } else if (dockSide == DOCKED_RIGHT) {
+ bounds.left -= insets.right;
+ }
+ return bounds;
+ }
+
+ /**
+ * Returns the mapped rect to the given dimensions.
+ */
+ private void getMappedRect(RectF bounds, int width, int height, Rect out) {
+ out.set((int) (bounds.left * width), (int) (bounds.top * height),
+ (int) (bounds.right * width), (int) (bounds.bottom * height));
+ }
}
// A comparator that sorts tasks by their freeform state
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java b/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java
index 3ad368c..f2a6310 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java
@@ -16,6 +16,8 @@
package com.android.systemui.recents.views;
+import android.graphics.Rect;
+
/**
* Represents a drop target for a drag gesture.
*/
@@ -25,5 +27,5 @@
* Returns whether this target can accept this drop. The x,y are relative to the top level
* RecentsView, and the width/height are of the RecentsView.
*/
- boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget);
+ boolean acceptsDrop(int x, int y, int width, int height, Rect insets, boolean isCurrentTarget);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index a893910..24ef433 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -104,7 +104,7 @@
private boolean mLastTaskLaunchedWasFreeform;
@ViewDebug.ExportedProperty(category="recents")
- private Rect mSystemInsets = new Rect();
+ Rect mSystemInsets = new Rect();
private int mDividerSize;
private Drawable mBackgroundScrim = new ColorDrawable(
@@ -140,8 +140,6 @@
LayoutInflater inflater = LayoutInflater.from(context);
if (RecentsDebugFlags.Static.EnableStackActionButton) {
- float cornerRadius = context.getResources().getDimensionPixelSize(
- R.dimen.recents_task_view_rounded_corners_radius);
mStackActionButton = (TextView) inflater.inflate(R.layout.recents_stack_action_button,
this, false);
mStackActionButton.setOnClickListener(new View.OnClickListener() {
@@ -151,13 +149,6 @@
}
});
addView(mStackActionButton);
- mStackActionButton.setClipToOutline(true);
- mStackActionButton.setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), cornerRadius);
- }
- });
}
mEmptyView = (TextView) inflater.inflate(R.layout.recents_empty, this, false);
addView(mEmptyView);
@@ -748,9 +739,10 @@
? overrideHintAlpha
: viewState.hintTextAlpha;
Rect bounds = isDefaultDockState
- ? dockState.getPreDockedBounds(getMeasuredWidth(), getMeasuredHeight())
+ ? dockState.getPreDockedBounds(getMeasuredWidth(), getMeasuredHeight(),
+ mSystemInsets)
: dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight(),
- mDividerSize, mSystemInsets, getResources());
+ mDividerSize, mSystemInsets, getResources());
if (viewState.dockAreaOverlay.getCallback() != this) {
viewState.dockAreaOverlay.setCallback(this);
viewState.dockAreaOverlay.setBounds(bounds);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index c692a16..636d4d6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -46,7 +46,7 @@
*/
class DockRegion {
public static TaskStack.DockState[] PHONE_LANDSCAPE = {
- // We only allow docking to the left for now on small devices
+ // We only allow docking to the left in landscape for now on small devices
TaskStack.DockState.LEFT
};
public static TaskStack.DockState[] PHONE_PORTRAIT = {
@@ -113,10 +113,11 @@
boolean isLandscape = mRv.getResources().getConfiguration().orientation ==
Configuration.ORIENTATION_LANDSCAPE;
RecentsConfiguration config = Recents.getConfiguration();
- TaskStack.DockState[] dockStates = isLandscape ?
- (config.isLargeScreen ? DockRegion.TABLET_LANDSCAPE : DockRegion.PHONE_LANDSCAPE) :
- (config.isLargeScreen ? DockRegion.TABLET_PORTRAIT : DockRegion.PHONE_PORTRAIT);
- return dockStates;
+ if (config.isLargeScreen) {
+ return isLandscape ? DockRegion.TABLET_LANDSCAPE : DockRegion.TABLET_PORTRAIT;
+ } else {
+ return isLandscape ? DockRegion.PHONE_LANDSCAPE : DockRegion.PHONE_PORTRAIT;
+ }
}
/**
@@ -226,7 +227,7 @@
// Give priority to the current drop target to retain the touch handling
if (mLastDropTarget != null) {
if (mLastDropTarget.acceptsDrop((int) evX, (int) evY, width, height,
- true /* isCurrentTarget */)) {
+ mRv.mSystemInsets, true /* isCurrentTarget */)) {
currentDropTarget = mLastDropTarget;
}
}
@@ -235,7 +236,7 @@
if (currentDropTarget == null) {
for (DropTarget target : mDropTargets) {
if (target.acceptsDrop((int) evX, (int) evY, width, height,
- false /* isCurrentTarget */)) {
+ mRv.mSystemInsets, false /* isCurrentTarget */)) {
currentDropTarget = target;
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index e3fe1ab..702b076d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -556,7 +556,9 @@
Math.max(0, mUnfocusedRange.getAbsoluteX(maxBottomNormX)));
boolean scrollToFront = launchState.launchedFromHome ||
launchState.launchedViaDockGesture;
- if (launchState.launchedWithAltTab) {
+ if (launchState.launchedFromBlacklistedApp) {
+ mInitialScrollP = mMaxScrollP;
+ } else if (launchState.launchedWithAltTab) {
mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
} else if (scrollToFront) {
mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
@@ -579,6 +581,7 @@
mTaskIndexOverrideMap.clear();
boolean scrollToFront = launchState.launchedFromHome ||
+ launchState.launchedFromBlacklistedApp ||
launchState.launchedViaDockGesture;
if (getInitialFocusState() == STATE_UNFOCUSED && mNumStackTasks > 1) {
if (ignoreScrollToFront || (!launchState.launchedWithAltTab && !scrollToFront)) {
@@ -1058,9 +1061,9 @@
* top and right system insets (but not the bottom inset) and left/right paddings, but _not_
* the top/bottom padding or insets.
*/
- public void getTaskStackBounds(Rect displayRect, Rect windowRect, int topInset, int rightInset,
- Rect taskStackBounds) {
- taskStackBounds.set(windowRect.left, windowRect.top + topInset,
+ public void getTaskStackBounds(Rect displayRect, Rect windowRect, int topInset, int leftInset,
+ int rightInset, Rect taskStackBounds) {
+ taskStackBounds.set(windowRect.left + leftInset, windowRect.top + topInset,
windowRect.right - rightInset, windowRect.bottom);
// Ensure that the new width is at most the smaller display edge size
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 586a8bc..24e75ac 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -218,7 +218,8 @@
// The drop targets for a task drag
private DropTarget mFreeformWorkspaceDropTarget = new DropTarget() {
@Override
- public boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget) {
+ public boolean acceptsDrop(int x, int y, int width, int height, Rect insets,
+ boolean isCurrentTarget) {
// This drop target has a fixed bounds and should be checked last, so just fall through
// if it is the current target
if (!isCurrentTarget) {
@@ -230,7 +231,8 @@
private DropTarget mStackDropTarget = new DropTarget() {
@Override
- public boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget) {
+ public boolean acceptsDrop(int x, int y, int width, int height, Rect insets,
+ boolean isCurrentTarget) {
// This drop target has a fixed bounds and should be checked last, so just fall through
// if it is the current target
if (!isCurrentTarget) {
@@ -1191,7 +1193,8 @@
// bounds have changed. This is because we may get spurious measures while dragging where
// our current stack bounds reflect the target drop region.
mLayoutAlgorithm.getTaskStackBounds(mDisplayRect, new Rect(0, 0, width, height),
- mLayoutAlgorithm.mSystemInsets.top, mLayoutAlgorithm.mSystemInsets.right, mTmpRect);
+ mLayoutAlgorithm.mSystemInsets.top, mLayoutAlgorithm.mSystemInsets.left,
+ mLayoutAlgorithm.mSystemInsets.right, mTmpRect);
if (!mTmpRect.equals(mStableStackBounds)) {
mStableStackBounds.set(mTmpRect);
mStackBounds.set(mTmpRect);
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 3193759..c46adf1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -164,6 +164,7 @@
/** Sets the thumbnail to a given bitmap. */
void setThumbnail(Bitmap bm, ActivityManager.TaskThumbnailInfo thumbnailInfo) {
if (bm != null) {
+ bm.prepareToDraw();
mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mDrawPaint.setShader(mBitmapShader);
mThumbnailRect.set(0, 0, bm.getWidth(), bm.getHeight());
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 4badc42..f3bae20 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -23,6 +23,8 @@
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
+import android.os.UserManager;
+import android.util.Log;
import android.view.WindowManager;
public class TakeScreenshotService extends Service {
@@ -44,6 +46,16 @@
}
}
};
+
+ // If the storage for this user is locked, we have no place to store
+ // the screenshot, so skip taking it instead of showing a misleading
+ // animation and error notification.
+ if (!getSystemService(UserManager.class).isUserUnlocked()) {
+ Log.w(TAG, "Skipping screenshot because storage is locked!");
+ post(finisher);
+ return;
+ }
+
if (mScreenshot == null) {
mScreenshot = new GlobalScreenshot(TakeScreenshotService.this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
index d5131be..1c6fffb 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -23,6 +23,8 @@
import android.os.AsyncTask;
import android.os.Handler;
import android.os.IPowerManager;
+import android.os.Looper;
+import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -45,6 +47,12 @@
*/
private static final float BRIGHTNESS_ADJ_RESOLUTION = 2048;
+ private static final int MSG_UPDATE_ICON = 0;
+ private static final int MSG_UPDATE_SLIDER = 1;
+ private static final int MSG_SET_CHECKED = 2;
+ private static final int MSG_ATTACH_LISTENER = 3;
+ private static final int MSG_DETACH_LISTENER = 4;
+
private final int mMinimumBacklight;
private final int mMaximumBacklight;
@@ -54,13 +62,14 @@
private final boolean mAutomaticAvailable;
private final IPowerManager mPower;
private final CurrentUserTracker mUserTracker;
- private final Handler mHandler;
+
+ private Handler mBackgroundHandler;
private final BrightnessObserver mBrightnessObserver;
private ArrayList<BrightnessStateChangeCallback> mChangeCallbacks =
new ArrayList<BrightnessStateChangeCallback>();
- private boolean mAutomatic;
+ private volatile boolean mAutomatic;
private boolean mListening;
private boolean mExternalChange;
@@ -90,24 +99,20 @@
@Override
public void onChange(boolean selfChange, Uri uri) {
if (selfChange) return;
- try {
- mExternalChange = true;
- if (BRIGHTNESS_MODE_URI.equals(uri)) {
- updateMode();
- updateSlider();
- } else if (BRIGHTNESS_URI.equals(uri) && !mAutomatic) {
- updateSlider();
- } else if (BRIGHTNESS_ADJ_URI.equals(uri) && mAutomatic) {
- updateSlider();
- } else {
- updateMode();
- updateSlider();
- }
- for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
- cb.onBrightnessLevelChanged();
- }
- } finally {
- mExternalChange = false;
+
+ if (BRIGHTNESS_MODE_URI.equals(uri)) {
+ mBackgroundHandler.post(mUpdateModeRunnable);
+ mBackgroundHandler.post(mUpdateSliderRunnable);
+ } else if (BRIGHTNESS_URI.equals(uri) && !mAutomatic) {
+ mBackgroundHandler.post(mUpdateSliderRunnable);
+ } else if (BRIGHTNESS_ADJ_URI.equals(uri) && mAutomatic) {
+ mBackgroundHandler.post(mUpdateSliderRunnable);
+ } else {
+ mBackgroundHandler.post(mUpdateModeRunnable);
+ mBackgroundHandler.post(mUpdateSliderRunnable);
+ }
+ for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
+ cb.onBrightnessLevelChanged();
}
}
@@ -132,16 +137,117 @@
}
+ private final Runnable mStartListeningRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mBrightnessObserver.startObserving();
+ mUserTracker.startTracking();
+
+ // Update the slider and mode before attaching the listener so we don't
+ // receive the onChanged notifications for the initial values.
+ mUpdateModeRunnable.run();
+ mUpdateSliderRunnable.run();
+
+ mHandler.sendEmptyMessage(MSG_ATTACH_LISTENER);
+ }
+ };
+
+ private final Runnable mStopListeningRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mBrightnessObserver.stopObserving();
+ mUserTracker.stopTracking();
+
+ mHandler.sendEmptyMessage(MSG_DETACH_LISTENER);
+ }
+ };
+
+ /**
+ * Fetch the brightness mode from the system settings and update the icon. Should be called from
+ * background thread.
+ */
+ private final Runnable mUpdateModeRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mAutomaticAvailable) {
+ int automatic;
+ automatic = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
+ UserHandle.USER_CURRENT);
+ mAutomatic = automatic != Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
+ mHandler.obtainMessage(MSG_UPDATE_ICON, mAutomatic ? 1 : 0).sendToTarget();
+ } else {
+ mHandler.obtainMessage(MSG_SET_CHECKED, 0).sendToTarget();
+ mHandler.obtainMessage(MSG_UPDATE_ICON, 0 /* automatic */).sendToTarget();
+ }
+ }
+ };
+
+ /**
+ * Fetch the brightness from the system settings and update the slider. Should be called from
+ * background thread.
+ */
+ private final Runnable mUpdateSliderRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mAutomatic) {
+ float value = Settings.System.getFloatForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0,
+ UserHandle.USER_CURRENT);
+ mHandler.obtainMessage(MSG_UPDATE_SLIDER, (int) BRIGHTNESS_ADJ_RESOLUTION,
+ (int) ((value + 1) * BRIGHTNESS_ADJ_RESOLUTION / 2f)).sendToTarget();
+ } else {
+ int value;
+ value = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS, mMaximumBacklight,
+ UserHandle.USER_CURRENT);
+ mHandler.obtainMessage(MSG_UPDATE_SLIDER, mMaximumBacklight - mMinimumBacklight,
+ value - mMinimumBacklight).sendToTarget();
+ }
+ }
+ };
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ mExternalChange = true;
+ try {
+ switch (msg.what) {
+ case MSG_UPDATE_ICON:
+ updateIcon(msg.arg1 != 0);
+ break;
+ case MSG_UPDATE_SLIDER:
+ mControl.setMax(msg.arg1);
+ mControl.setValue(msg.arg2);
+ break;
+ case MSG_SET_CHECKED:
+ mControl.setChecked(msg.arg1 != 0);
+ break;
+ case MSG_ATTACH_LISTENER:
+ mControl.setOnChangedListener(BrightnessController.this);
+ break;
+ case MSG_DETACH_LISTENER:
+ mControl.setOnChangedListener(null);
+ default:
+ super.handleMessage(msg);
+ }
+ } finally {
+ mExternalChange = false;
+ }
+ }
+ };
+
public BrightnessController(Context context, ImageView icon, ToggleSlider control) {
mContext = context;
mIcon = icon;
mControl = control;
- mHandler = new Handler();
+ mBackgroundHandler = new Handler(Looper.getMainLooper());
mUserTracker = new CurrentUserTracker(mContext) {
@Override
public void onUserSwitched(int newUserId) {
- updateMode();
- updateSlider();
+ mBackgroundHandler.post(mUpdateModeRunnable);
+ mBackgroundHandler.post(mUpdateSliderRunnable);
}
};
mBrightnessObserver = new BrightnessObserver(mHandler);
@@ -155,6 +261,10 @@
mPower = IPowerManager.Stub.asInterface(ServiceManager.getService("power"));
}
+ public void setBackgroundLooper(Looper backgroundLooper) {
+ mBackgroundHandler = new Handler(backgroundLooper);
+ }
+
public void addStateChangedCallback(BrightnessStateChangeCallback cb) {
mChangeCallbacks.add(cb);
}
@@ -173,15 +283,7 @@
return;
}
- mBrightnessObserver.startObserving();
- mUserTracker.startTracking();
-
- // Update the slider and mode before attaching the listener so we don't
- // receive the onChanged notifications for the initial values.
- updateMode();
- updateSlider();
-
- mControl.setOnChangedListener(this);
+ mBackgroundHandler.post(mStartListeningRunnable);
mListening = true;
}
@@ -191,9 +293,7 @@
return;
}
- mBrightnessObserver.stopObserving();
- mUserTracker.stopTracking();
- mControl.setOnChangedListener(null);
+ mBackgroundHandler.post(mStopListeningRunnable);
mListening = false;
}
@@ -267,39 +367,4 @@
com.android.systemui.R.drawable.ic_qs_brightness_auto_off);
}
}
-
- /** Fetch the brightness mode from the system settings and update the icon */
- private void updateMode() {
- if (mAutomaticAvailable) {
- int automatic;
- automatic = Settings.System.getIntForUser(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_MODE,
- Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
- UserHandle.USER_CURRENT);
- mAutomatic = automatic != Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
- updateIcon(mAutomatic);
- } else {
- mControl.setChecked(false);
- updateIcon(false /*automatic*/);
- }
- }
-
- /** Fetch the brightness from the system settings and update the slider */
- private void updateSlider() {
- if (mAutomatic) {
- float value = Settings.System.getFloatForUser(mContext.getContentResolver(),
- Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0,
- UserHandle.USER_CURRENT);
- mControl.setMax((int) BRIGHTNESS_ADJ_RESOLUTION);
- mControl.setValue((int) ((value + 1) * BRIGHTNESS_ADJ_RESOLUTION / 2f));
- } else {
- int value;
- value = Settings.System.getIntForUser(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS, mMaximumBacklight,
- UserHandle.USER_CURRENT);
- mControl.setMax(mMaximumBacklight - mMinimumBacklight);
- mControl.setValue(value - mMinimumBacklight);
- }
- }
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 998f50f..cb77d7b 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -31,6 +31,7 @@
import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Message;
import android.util.AttributeSet;
import android.view.Display;
import android.view.DisplayInfo;
@@ -106,6 +107,11 @@
private static final Interpolator IME_ADJUST_INTERPOLATOR =
new PathInterpolator(0.2f, 0f, 0.1f, 1f);
+ private static final long ONE_MS_IN_NS = 1000000;
+ private static final long ONE_S_IN_NS = ONE_MS_IN_NS * 1000;
+
+ private static final int MSG_RESIZE_STACK = 0;
+
private DividerHandleView mHandle;
private View mBackground;
private MinimizedDockShadow mMinimizedShadow;
@@ -150,7 +156,25 @@
private boolean mDockedStackMinimized;
private boolean mAdjustedForIme;
private DividerState mState;
- private final Handler mHandler = new Handler();
+
+ /**
+ * The offset between vsync-app and vsync-surfaceflinger. See
+ * {@link #calculateAppSurfaceFlingerVsyncOffsetMs} why this is necessary.
+ */
+ private long mSurfaceFlingerOffsetMs;
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_RESIZE_STACK:
+ resizeStack(msg.arg1, msg.arg2, (SnapTarget) msg.obj);
+ break;
+ default:
+ super.handleMessage(msg);
+ }
+ }
+ };
private final AccessibilityDelegate mHandleDelegate = new AccessibilityDelegate() {
@Override
@@ -290,6 +314,7 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
EventBus.getDefault().register(this);
+ mSurfaceFlingerOffsetMs = calculateAppSurfaceFlingerVsyncOffsetMs();
}
@Override
@@ -298,6 +323,25 @@
EventBus.getDefault().unregister(this);
}
+ /**
+ * This method calculates the offset between vsync-surfaceflinger and vsync-app. If vsync-app
+ * is a couple of milliseconds before vsync-sf, a touch or animation event that causes the
+ * stacks to be resized are sometimes processed before the vsync-sf tick, and sometimes after,
+ * which leads to jank. Figure out this difference here and then post all the touch/animation
+ * events to start being processed at vsync-sf.
+ *
+ * @return The offset between vsync-app and vsync-sf, or 0 if vsync app happens after vsync-sf.
+ */
+ private long calculateAppSurfaceFlingerVsyncOffsetMs() {
+ Display display = getDisplay();
+
+ // Calculate vsync offset from SurfaceFlinger.
+ // See frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:getDisplayConfigs
+ long vsyncPeriod = (long) (ONE_S_IN_NS / display.getRefreshRate());
+ long sfVsyncOffset = vsyncPeriod - (display.getPresentationDeadlineNanos() - ONE_MS_IN_NS);
+ return Math.max(0, (sfVsyncOffset - display.getAppVsyncOffsetNanos()) / ONE_MS_IN_NS);
+ }
+
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
if (mStableInsets.left != insets.getStableInsetLeft()
@@ -453,7 +497,7 @@
if (mMoving && mDockSide != WindowManager.DOCKED_INVALID) {
SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget(
mStartPosition, 0 /* velocity */, false /* hardDismiss */);
- resizeStack(calculatePosition(x, y), mStartPosition, snapTarget);
+ resizeStackDelayed(calculatePosition(x, y), mStartPosition, snapTarget);
}
break;
case MotionEvent.ACTION_UP:
@@ -532,10 +576,11 @@
final long endDelay) {
final boolean taskPositionSameAtEnd = snapTarget.flag == SnapTarget.FLAG_NONE;
ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position);
- anim.addUpdateListener(animation -> resizeStack((Integer) animation.getAnimatedValue(),
+ anim.addUpdateListener(animation -> resizeStackDelayed((int) animation.getAnimatedValue(),
taskPositionSameAtEnd && animation.getAnimatedFraction() == 1f
? TASK_POSITION_SAME
- : snapTarget.taskPosition, snapTarget));
+ : snapTarget.taskPosition,
+ snapTarget));
Runnable endAction = () -> {
commitSnapFlags(snapTarget);
mWindowManagerProxy.setResizing(false);
@@ -551,15 +596,24 @@
@Override
public void onAnimationCancel(Animator animation) {
+ mHandler.removeMessages(MSG_RESIZE_STACK);
mCancelled = true;
}
@Override
public void onAnimationEnd(Animator animation) {
- if (endDelay == 0 || mCancelled) {
+ long delay = 0;
+ if (endDelay != 0) {
+ delay = endDelay;
+ } else if (mCancelled) {
+ delay = 0;
+ } else if (mSurfaceFlingerOffsetMs != 0) {
+ delay = mSurfaceFlingerOffsetMs;
+ }
+ if (delay == 0) {
endAction.run();
} else {
- mHandler.postDelayed(endAction, endDelay);
+ mHandler.postDelayed(endAction, delay);
}
}
});
@@ -793,6 +847,17 @@
mDisplayHeight, mDividerSize);
}
+ public void resizeStackDelayed(int position, int taskPosition, SnapTarget taskSnapTarget) {
+ if (mSurfaceFlingerOffsetMs != 0) {
+ Message message = mHandler.obtainMessage(MSG_RESIZE_STACK, position, taskPosition,
+ taskSnapTarget);
+ message.setAsynchronous(true);
+ mHandler.sendMessageDelayed(message, mSurfaceFlingerOffsetMs);
+ } else {
+ resizeStack(position, taskPosition, taskSnapTarget);
+ }
+ }
+
public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) {
calculateBoundsForPosition(position, mDockSide, mDockedRect);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 42c9a126..e35ef44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -137,11 +137,13 @@
private float mNormalBackgroundVisibilityAmount;
private ValueAnimator mFadeInFromDarkAnimator;
+ private float mDimmedBackgroundFadeInAmount = -1;
private ValueAnimator.AnimatorUpdateListener mBackgroundVisibilityUpdater
= new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
setNormalBackgroundVisibilityAmount(mBackgroundNormal.getAlpha());
+ mDimmedBackgroundFadeInAmount = mBackgroundDimmed.getAlpha();
}
};
private AnimatorListenerAdapter mFadeInEndListener = new AnimatorListenerAdapter() {
@@ -149,6 +151,7 @@
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mFadeInFromDarkAnimator = null;
+ mDimmedBackgroundFadeInAmount = -1;
updateBackground();
}
};
@@ -597,7 +600,10 @@
}
protected void updateBackgroundAlpha(float transformationAmount) {
- mBgAlpha = isChildInGroup() && mDimmed ? transformationAmount : 1f;
+ mBgAlpha = isChildInGroup() && mDimmed ? transformationAmount : 1f;
+ if (mDimmedBackgroundFadeInAmount != -1) {
+ mBgAlpha *= mDimmedBackgroundFadeInAmount;
+ }
mBackgroundDimmed.setAlpha(mBgAlpha);
}
@@ -738,6 +744,7 @@
}
if (!mWasCancelled) {
enableAppearDrawing(false);
+ onAppearAnimationFinished(isAppearing);
}
}
@@ -754,6 +761,9 @@
mAppearAnimator.start();
}
+ protected void onAppearAnimationFinished(boolean wasAppearing) {
+ }
+
private void cancelAppearAnimation() {
if (mAppearAnimator != null) {
mAppearAnimator.cancel();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index e224141..a1854fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -471,20 +471,28 @@
}
}
- riv.setVisibility(View.VISIBLE);
- int cx = view.getLeft() + view.getWidth() / 2;
+ int width = view.getWidth();
+ if (view instanceof TextView) {
+ // Center the reveal on the text which might be off-center from the TextView
+ TextView tv = (TextView) view;
+ if (tv.getLayout() != null) {
+ int innerWidth = (int) tv.getLayout().getLineWidth(0);
+ innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight();
+ width = Math.min(width, innerWidth);
+ }
+ }
+ int cx = view.getLeft() + width / 2;
int cy = view.getTop() + view.getHeight() / 2;
int w = riv.getWidth();
int h = riv.getHeight();
int r = Math.max(
Math.max(cx + cy, cx + (h - cy)),
Math.max((w - cx) + cy, (w - cx) + (h - cy)));
- ViewAnimationUtils.createCircularReveal(riv, cx, cy, 0, r)
- .start();
+ riv.setRevealParameters(cx, cy, r);
riv.setPendingIntent(pendingIntent);
riv.setRemoteInput(inputs, input);
- riv.focus();
+ riv.focusAnimated();
return true;
}
@@ -953,7 +961,6 @@
mNotificationGutsExposed = entry.row.getGuts();
bindGuts(entry.row);
}
- entry.cacheContentViews(mContext, null /* updatedNotification */);
inflateViews(entry, mStackScroller);
}
}
@@ -1577,7 +1584,12 @@
entry.notification.getUser().getIdentifier());
final StatusBarNotification sbn = entry.notification;
- entry.cacheContentViews(mContext, null);
+ try {
+ entry.cacheContentViews(mContext, null);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Unable to get notification remote views", e);
+ return false;
+ }
final RemoteViews contentView = entry.cachedContentView;
final RemoteViews bigContentView = entry.cachedBigContentView;
@@ -2346,7 +2358,13 @@
Notification n = notification.getNotification();
mNotificationData.updateRanking(ranking);
- boolean applyInPlace = entry.cacheContentViews(mContext, notification.getNotification());
+ boolean applyInPlace;
+ try {
+ applyInPlace = entry.cacheContentViews(mContext, notification.getNotification());
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Unable to get notification remote views", e);
+ applyInPlace = false;
+ }
boolean shouldPeek = shouldPeek(entry, notification);
boolean alertAgain = alertAgain(entry, n);
if (DEBUG) {
@@ -2398,7 +2416,10 @@
StatusBarIconView.contentDescForNotification(mContext, n));
entry.icon.setNotification(n);
entry.icon.set(ic);
- inflateViews(entry, mStackScroller);
+ if (!inflateViews(entry, mStackScroller)) {
+ handleNotificationError(notification, "Couldn't update remote views for: "
+ + notification);
+ }
}
updateHeadsUp(key, entry, shouldPeek, alertAgain);
updateNotifications();
@@ -2496,8 +2517,7 @@
boolean inUse = mPowerManager.isScreenOn()
&& (!mStatusBarKeyguardViewManager.isShowing()
- || mStatusBarKeyguardViewManager.isOccluded())
- && !mStatusBarKeyguardViewManager.isInputRestricted();
+ || mStatusBarKeyguardViewManager.isOccluded());
try {
inUse = inUse && !mDreamManager.isDreaming();
} catch (RemoteException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 7b23c80..419c91b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -23,6 +23,7 @@
import android.os.IBinder;
import android.os.Message;
import android.util.Pair;
+import android.view.KeyEvent;
import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.IStatusBar;
@@ -75,6 +76,7 @@
private static final int MSG_TOGGLE_APP_SPLIT_SCREEN = 30 << MSG_SHIFT;
private static final int MSG_APP_TRANSITION_FINISHED = 31 << MSG_SHIFT;
private static final int MSG_DISMISS_KEYBOARD_SHORTCUTS = 32 << MSG_SHIFT;
+ private static final int MSG_HANDLE_SYSNAV_KEY = 33 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -129,6 +131,8 @@
void addQsTile(ComponentName tile);
void remQsTile(ComponentName tile);
void clickTile(ComponentName tile);
+
+ void handleSystemNavigationKey(int arg1);
}
public CommandQueue(Callbacks callbacks) {
@@ -388,6 +392,13 @@
}
}
+ @Override
+ public void handleSystemNavigationKey(int key) {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_HANDLE_SYSNAV_KEY, key, 0).sendToTarget();
+ }
+ }
+
private final class H extends Handler {
public void handleMessage(Message msg) {
final int what = msg.what & MSG_MASK;
@@ -503,6 +514,9 @@
case MSG_TOGGLE_APP_SPLIT_SCREEN:
mCallbacks.toggleSplitScreen();
break;
+ case MSG_HANDLE_SYSNAV_KEY:
+ mCallbacks.handleSystemNavigationKey(msg.arg1);
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index e400506..02fdd3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -598,7 +598,7 @@
}
private NotificationHeaderView getVisibleNotificationHeader() {
- if (mIsSummaryWithChildren) {
+ if (mIsSummaryWithChildren && !mShowingPublic) {
return mChildrenContainer.getHeaderView();
}
return getShowingLayout().getVisibleNotificationHeader();
@@ -1122,7 +1122,7 @@
mPrivateLayout.setUserExpanding(userLocked);
if (mIsSummaryWithChildren) {
mChildrenContainer.setUserLocked(userLocked);
- if (userLocked || (!userLocked && !isGroupExpanded())) {
+ if (userLocked || !isGroupExpanded()) {
updateBackgroundForGroupState();
}
}
@@ -1175,7 +1175,20 @@
* @see #canViewBeDismissed()
*/
public boolean isClearable() {
- return mStatusBarNotification != null && mStatusBarNotification.isClearable();
+ if (mStatusBarNotification == null || !mStatusBarNotification.isClearable()) {
+ return false;
+ }
+ if (mIsSummaryWithChildren) {
+ List<ExpandableNotificationRow> notificationChildren =
+ mChildrenContainer.getNotificationChildren();
+ for (int i = 0; i < notificationChildren.size(); i++) {
+ ExpandableNotificationRow child = notificationChildren.get(i);
+ if (!child.isClearable()) {
+ return false;
+ }
+ }
+ }
+ return true;
}
@Override
@@ -1429,13 +1442,30 @@
@Override
protected View getContentView() {
- if (mIsSummaryWithChildren) {
+ if (mIsSummaryWithChildren && !mShowingPublic) {
return mChildrenContainer;
}
return getShowingLayout();
}
@Override
+ protected void onAppearAnimationFinished(boolean wasAppearing) {
+ super.onAppearAnimationFinished(wasAppearing);
+ if (wasAppearing) {
+ // During the animation the visible view might have changed, so let's make sure all
+ // alphas are reset
+ if (mChildrenContainer != null) {
+ mChildrenContainer.setAlpha(1.0f);
+ mChildrenContainer.setLayerType(LAYER_TYPE_NONE, null);
+ }
+ mPrivateLayout.setAlpha(1.0f);
+ mPrivateLayout.setLayerType(LAYER_TYPE_NONE, null);
+ mPublicLayout.setAlpha(1.0f);
+ mPublicLayout.setLayerType(LAYER_TYPE_NONE, null);
+ }
+ }
+
+ @Override
public int getExtraBottomPadding() {
if (mIsSummaryWithChildren && isGroupExpanded()) {
return mIncreasedPaddingBetweenElements;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 79e06c6..016e1f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AlertDialog;
import android.app.AppGlobals;
import android.app.Dialog;
@@ -45,15 +47,18 @@
import android.view.KeyboardShortcutInfo;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.View.AccessibilityDelegate;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager.KeyboardShortcutsReceiver;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.android.internal.app.AssistUtils;
+import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
@@ -63,6 +68,7 @@
import java.util.List;
import static android.content.Context.LAYOUT_INFLATER_SERVICE;
+import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
/**
@@ -72,7 +78,6 @@
private static final String TAG = KeyboardShortcuts.class.getSimpleName();
private static final Object sLock = new Object();
private static KeyboardShortcuts sInstance;
- private static boolean sIsShowing;
private final SparseArray<String> mSpecialCharacterNames = new SparseArray<>();
private final SparseArray<String> mModifierNames = new SparseArray<>();
@@ -113,7 +118,7 @@
private KeyCharacterMap mKeyCharacterMap;
private KeyboardShortcuts(Context context) {
- this.mContext = new ContextThemeWrapper(context, android.R.style.Theme_Material_Light);
+ this.mContext = new ContextThemeWrapper(context, android.R.style.Theme_DeviceDefault_Light);
this.mPackageManager = AppGlobals.getPackageManager();
loadResources(context);
}
@@ -131,13 +136,12 @@
dismiss();
}
getInstance(context).showKeyboardShortcuts(deviceId);
- sIsShowing = true;
}
}
public static void toggle(Context context, int deviceId) {
synchronized (sLock) {
- if (sIsShowing) {
+ if (isShowing()) {
dismiss();
} else {
show(context, deviceId);
@@ -151,10 +155,14 @@
sInstance.dismissKeyboardShortcuts();
sInstance = null;
}
- sIsShowing = false;
}
}
+ private static boolean isShowing() {
+ return sInstance != null && sInstance.mKeyboardShortcutsDialog != null
+ && sInstance.mKeyboardShortcutsDialog.isShowing();
+ }
+
private void loadResources(Context context) {
mSpecialCharacterNames.put(
KeyEvent.KEYCODE_HOME, context.getString(R.string.keyboard_key_home));
@@ -580,7 +588,7 @@
R.layout.keyboard_shortcuts_category_title, keyboardShortcutsLayout, false);
categoryTitle.setText(group.getLabel());
categoryTitle.setTextColor(group.isSystemGroup()
- ? mContext.getColor(R.color.ksh_system_group_color)
+ ? Utils.getColorAccent(mContext)
: mContext.getColor(R.color.ksh_application_group_color));
keyboardShortcutsLayout.addView(categoryTitle);
@@ -589,7 +597,7 @@
final int itemsSize = group.getItems().size();
for (int j = 0; j < itemsSize; j++) {
KeyboardShortcutInfo info = group.getItems().get(j);
- List<StringOrDrawable> shortcutKeys = getHumanReadableShortcutKeys(info);
+ List<StringDrawableContainer> shortcutKeys = getHumanReadableShortcutKeys(info);
if (shortcutKeys == null) {
// Ignore shortcuts we can't display keys for.
Log.w(TAG, "Keyboard Shortcut contains unsupported keys, skipping.");
@@ -619,25 +627,33 @@
.findViewById(R.id.keyboard_shortcuts_item_container);
final int shortcutKeysSize = shortcutKeys.size();
for (int k = 0; k < shortcutKeysSize; k++) {
- StringOrDrawable shortcutRepresentation = shortcutKeys.get(k);
- if (shortcutRepresentation.drawable != null) {
+ StringDrawableContainer shortcutRepresentation = shortcutKeys.get(k);
+ if (shortcutRepresentation.mDrawable != null) {
ImageView shortcutKeyIconView = (ImageView) inflater.inflate(
R.layout.keyboard_shortcuts_key_icon_view, shortcutItemsContainer,
false);
Bitmap bitmap = Bitmap.createBitmap(shortcutKeyIconItemHeightWidth,
shortcutKeyIconItemHeightWidth, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
- shortcutRepresentation.drawable.setBounds(0, 0, canvas.getWidth(),
+ shortcutRepresentation.mDrawable.setBounds(0, 0, canvas.getWidth(),
canvas.getHeight());
- shortcutRepresentation.drawable.draw(canvas);
+ shortcutRepresentation.mDrawable.draw(canvas);
shortcutKeyIconView.setImageBitmap(bitmap);
+ shortcutKeyIconView.setImportantForAccessibility(
+ IMPORTANT_FOR_ACCESSIBILITY_YES);
+ shortcutKeyIconView.setAccessibilityDelegate(
+ new ShortcutKeyAccessibilityDelegate(
+ shortcutRepresentation.mString));
shortcutItemsContainer.addView(shortcutKeyIconView);
- } else if (shortcutRepresentation.string != null) {
+ } else if (shortcutRepresentation.mString != null) {
TextView shortcutKeyTextView = (TextView) inflater.inflate(
R.layout.keyboard_shortcuts_key_view, shortcutItemsContainer,
false);
shortcutKeyTextView.setMinimumWidth(shortcutKeyTextItemMinWidth);
- shortcutKeyTextView.setText(shortcutRepresentation.string);
+ shortcutKeyTextView.setText(shortcutRepresentation.mString);
+ shortcutKeyTextView.setAccessibilityDelegate(
+ new ShortcutKeyAccessibilityDelegate(
+ shortcutRepresentation.mString));
shortcutItemsContainer.addView(shortcutKeyTextView);
}
}
@@ -653,19 +669,20 @@
}
}
- private List<StringOrDrawable> getHumanReadableShortcutKeys(KeyboardShortcutInfo info) {
- List<StringOrDrawable> shortcutKeys = getHumanReadableModifiers(info);
+ private List<StringDrawableContainer> getHumanReadableShortcutKeys(KeyboardShortcutInfo info) {
+ List<StringDrawableContainer> shortcutKeys = getHumanReadableModifiers(info);
if (shortcutKeys == null) {
return null;
}
- String displayLabelString = null;
- Drawable displayLabelDrawable = null;
+ String shortcutKeyString = null;
+ Drawable shortcutKeyDrawable = null;
if (info.getBaseCharacter() > Character.MIN_VALUE) {
- displayLabelString = String.valueOf(info.getBaseCharacter());
+ shortcutKeyString = String.valueOf(info.getBaseCharacter());
} else if (mSpecialCharacterDrawables.get(info.getKeycode()) != null) {
- displayLabelDrawable = mSpecialCharacterDrawables.get(info.getKeycode());
+ shortcutKeyDrawable = mSpecialCharacterDrawables.get(info.getKeycode());
+ shortcutKeyString = mSpecialCharacterNames.get(info.getKeycode());
} else if (mSpecialCharacterNames.get(info.getKeycode()) != null) {
- displayLabelString = mSpecialCharacterNames.get(info.getKeycode());
+ shortcutKeyString = mSpecialCharacterNames.get(info.getKeycode());
} else {
// Special case for shortcuts with no base key or keycode.
if (info.getKeycode() == KeyEvent.KEYCODE_UNKNOWN) {
@@ -673,22 +690,23 @@
}
char displayLabel = mKeyCharacterMap.getDisplayLabel(info.getKeycode());
if (displayLabel != 0) {
- displayLabelString = String.valueOf(displayLabel);
+ shortcutKeyString = String.valueOf(displayLabel);
} else {
return null;
}
}
- if (displayLabelDrawable != null) {
- shortcutKeys.add(new StringOrDrawable(displayLabelDrawable));
- } else if (displayLabelString != null) {
- shortcutKeys.add(new StringOrDrawable(displayLabelString.toUpperCase()));
+ if (shortcutKeyString != null) {
+ shortcutKeys.add(new StringDrawableContainer(shortcutKeyString, shortcutKeyDrawable));
+ } else {
+ Log.w(TAG, "Keyboard Shortcut does not have a text representation, skipping.");
}
+
return shortcutKeys;
}
- private List<StringOrDrawable> getHumanReadableModifiers(KeyboardShortcutInfo info) {
- final List<StringOrDrawable> shortcutKeys = new ArrayList<>();
+ private List<StringDrawableContainer> getHumanReadableModifiers(KeyboardShortcutInfo info) {
+ final List<StringDrawableContainer> shortcutKeys = new ArrayList<>();
int modifiers = info.getModifiers();
if (modifiers == 0) {
return shortcutKeys;
@@ -696,13 +714,9 @@
for(int i = 0; i < mModifierNames.size(); ++i) {
final int supportedModifier = mModifierNames.keyAt(i);
if ((modifiers & supportedModifier) != 0) {
- if (mModifierDrawables.get(supportedModifier) != null) {
- shortcutKeys.add(new StringOrDrawable(
- mModifierDrawables.get(supportedModifier)));
- } else {
- shortcutKeys.add(new StringOrDrawable(
- mModifierNames.get(supportedModifier).toUpperCase()));
- }
+ shortcutKeys.add(new StringDrawableContainer(
+ mModifierNames.get(supportedModifier),
+ mModifierDrawables.get(supportedModifier)));
modifiers &= ~supportedModifier;
}
}
@@ -713,16 +727,31 @@
return shortcutKeys;
}
- private static final class StringOrDrawable {
- public String string;
- public Drawable drawable;
+ private final class ShortcutKeyAccessibilityDelegate extends AccessibilityDelegate {
+ private String mContentDescription;
- public StringOrDrawable(String string) {
- this.string = string;
+ ShortcutKeyAccessibilityDelegate(String contentDescription) {
+ mContentDescription = contentDescription;
}
- public StringOrDrawable(Drawable drawable) {
- this.drawable = drawable;
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ if (mContentDescription != null) {
+ info.setContentDescription(mContentDescription.toLowerCase());
+ }
+ }
+ }
+
+ private static final class StringDrawableContainer {
+ @NonNull
+ public String mString;
+ @Nullable
+ public Drawable mDrawable;
+
+ StringDrawableContainer(String string, Drawable drawable) {
+ mString = string;
+ mDrawable = drawable;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index 0b1984d..88f37a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -147,7 +147,7 @@
@Override
protected void onDraw(Canvas canvas) {
- mSupportHardware = false;//canvas.isHardwareAccelerated();
+ mSupportHardware = canvas.isHardwareAccelerated();
drawBackgroundCircle(canvas);
canvas.save();
canvas.scale(mImageScale, mImageScale, getWidth() / 2, getHeight() / 2);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 71904fa..6d73ccb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar;
+import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -30,6 +31,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.os.UserManager;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.Log;
@@ -57,6 +59,7 @@
private final Context mContext;
private final KeyguardIndicationTextView mTextView;
+ private final UserManager mUserManager;
private final IBatteryStats mBatteryInfo;
private final int mSlowThreshold;
@@ -85,9 +88,10 @@
mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold);
mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold);
-
+ mUserManager = context.getSystemService(UserManager.class);
mBatteryInfo = IBatteryStats.Stub.asInterface(
ServiceManager.getService(BatteryStats.SERVICE_NAME));
+
KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitor);
context.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM,
new IntentFilter(Intent.ACTION_TIME_TICK), null, null);
@@ -155,30 +159,29 @@
private void updateIndication() {
if (mVisible) {
- mTextView.switchIndication(computeIndication());
- mTextView.setTextColor(computeColor());
- }
- }
+ // Walk down a precedence-ordered list of what should indication
+ // should be shown based on user or device state
+ if (!mUserManager.isUserUnlocked(ActivityManager.getCurrentUser())) {
+ mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked);
+ mTextView.setTextColor(Color.WHITE);
- private int computeColor() {
- if (!TextUtils.isEmpty(mTransientIndication)) {
- return mTransientTextColor;
- }
- return Color.WHITE;
- }
+ } else if (!TextUtils.isEmpty(mTransientIndication)) {
+ mTextView.switchIndication(mTransientIndication);
+ mTextView.setTextColor(mTransientTextColor);
- private String computeIndication() {
- if (!TextUtils.isEmpty(mTransientIndication)) {
- return mTransientIndication;
- }
- if (mPowerPluggedIn) {
- String indication = computePowerIndication();
- if (DEBUG_CHARGING_SPEED) {
- indication += ", " + (mChargingWattage / 1000) + " mW";
+ } else if (mPowerPluggedIn) {
+ String indication = computePowerIndication();
+ if (DEBUG_CHARGING_SPEED) {
+ indication += ", " + (mChargingWattage / 1000) + " mW";
+ }
+ mTextView.switchIndication(indication);
+ mTextView.setTextColor(Color.WHITE);
+
+ } else {
+ mTextView.switchIndication(mRestingIndication);
+ mTextView.setTextColor(Color.WHITE);
}
- return indication;
}
- return mRestingIndication;
}
private String computePowerIndication() {
@@ -225,6 +228,8 @@
}
KeyguardUpdateMonitorCallback mUpdateMonitor = new KeyguardUpdateMonitorCallback() {
+ public int mLastSuccessiveErrorMessage = -1;
+
@Override
public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
@@ -252,6 +257,9 @@
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_FP_MSG),
TRANSIENT_FP_ERROR_TIMEOUT);
}
+ // Help messages indicate that there was actually a try since the last error, so those
+ // are not two successive error messages anymore.
+ mLastSuccessiveErrorMessage = -1;
}
@Override
@@ -263,15 +271,22 @@
}
int errorColor = mContext.getResources().getColor(R.color.system_warning_color, null);
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- mStatusBarKeyguardViewManager.showBouncerMessage(errString, errorColor);
+ // When swiping up right after receiving a fingerprint error, the bouncer calls
+ // authenticate leading to the same message being shown again on the bouncer.
+ // We want to avoid this, as it may confuse the user when the message is too
+ // generic.
+ if (mLastSuccessiveErrorMessage != msgId) {
+ mStatusBarKeyguardViewManager.showBouncerMessage(errString, errorColor);
+ }
} else if (updateMonitor.isDeviceInteractive()) {
- showTransientIndication(errString, errorColor);
- // We want to keep this message around in case the screen was off
- mHandler.removeMessages(MSG_HIDE_TRANSIENT);
- hideTransientIndicationDelayed(5000);
- } else {
- mMessageToShowOnScreenOn = errString;
+ showTransientIndication(errString, errorColor);
+ // We want to keep this message around in case the screen was off
+ mHandler.removeMessages(MSG_HIDE_TRANSIENT);
+ hideTransientIndicationDelayed(5000);
+ } else {
+ mMessageToShowOnScreenOn = errString;
}
+ mLastSuccessiveErrorMessage = msgId;
}
@Override
@@ -293,6 +308,18 @@
mMessageToShowOnScreenOn = null;
}
}
+
+ @Override
+ public void onFingerprintAuthenticated(int userId) {
+ super.onFingerprintAuthenticated(userId);
+ mLastSuccessiveErrorMessage = -1;
+ }
+
+ @Override
+ public void onFingerprintAuthFailed() {
+ super.onFingerprintAuthFailed();
+ mLastSuccessiveErrorMessage = -1;
+ }
};
BroadcastReceiver mReceiver = new BroadcastReceiver() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 6570221..05a9fc7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -34,7 +34,6 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
-import java.util.Map;
import java.util.Objects;
/**
@@ -343,7 +342,6 @@
entry.notification.setOverrideGroupKey(overrideGroupKey);
mGroupManager.onEntryUpdated(entry, oldSbn);
}
- //mGroupManager.onEntryBundlingUpdated(entry, getOverrideGroupKey(entry.key));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 870e7f0..c497cfd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -186,7 +186,7 @@
try {
final PackageInfo info =
pm.getPackageInfo(sbn.getPackageName(), PackageManager.GET_SIGNATURES);
- systemApp = Utils.isSystemPackage(pm, info);
+ systemApp = Utils.isSystemPackage(getResources(), pm, info);
} catch (PackageManager.NameNotFoundException e) {
// unlikely.
}
@@ -266,7 +266,7 @@
}
private void bindSlider(final View importanceSlider, final boolean systemApp) {
- mActiveSliderTint = loadColorStateList(R.color.notification_guts_slider_color);
+ mActiveSliderTint = ColorStateList.valueOf(Utils.getColorAccent(mContext));
mInactiveSliderTint = loadColorStateList(R.color.notification_guts_disabled_slider_color);
mImportanceSummary = ((TextView) importanceSlider.findViewById(R.id.summary));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
index dba7130..bfa43fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
@@ -42,6 +42,7 @@
private float mViewAlpha = 1.0f;
private ValueAnimator mAlphaAnimator;
private Rect mExcludedRect = new Rect();
+ private int mLeftInset = 0;
private boolean mHasExcludedArea;
private ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener
= new ValueAnimator.AnimatorUpdateListener() {
@@ -87,12 +88,12 @@
if (mExcludedRect.top > 0) {
canvas.drawRect(0, 0, getWidth(), mExcludedRect.top, mPaint);
}
- if (mExcludedRect.left > 0) {
- canvas.drawRect(0, mExcludedRect.top, mExcludedRect.left, mExcludedRect.bottom,
- mPaint);
+ if (mExcludedRect.left + mLeftInset > 0) {
+ canvas.drawRect(0, mExcludedRect.top, mExcludedRect.left + mLeftInset,
+ mExcludedRect.bottom, mPaint);
}
- if (mExcludedRect.right < getWidth()) {
- canvas.drawRect(mExcludedRect.right,
+ if (mExcludedRect.right + mLeftInset < getWidth()) {
+ canvas.drawRect(mExcludedRect.right + mLeftInset,
mExcludedRect.top,
getWidth(),
mExcludedRect.bottom,
@@ -183,4 +184,14 @@
public void setChangeRunnable(Runnable changeRunnable) {
mChangeRunnable = changeRunnable;
}
+
+ public void setLeftInset(int leftInset) {
+ if (mLeftInset != leftInset) {
+ mLeftInset = leftInset;
+
+ if (mHasExcludedArea) {
+ invalidate();
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 599e575a..74caa53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -67,6 +67,8 @@
private boolean mNoSimsVisible = false;
private boolean mVpnVisible = false;
+ private int mVpnIconId = 0;
+ private int mLastVpnIconId = -1;
private boolean mEthernetVisible = false;
private int mEthernetIconId = 0;
private int mLastEthernetIconId = -1;
@@ -164,6 +166,7 @@
mSC = sc;
mSC.addCallback(this);
mVpnVisible = mSC.isVpnEnabled();
+ mVpnIconId = currentVpnIconId(mSC.isVpnBranded());
}
@Override
@@ -249,6 +252,7 @@
@Override
public void run() {
mVpnVisible = mSC.isVpnEnabled();
+ mVpnIconId = currentVpnIconId(mSC.isVpnBranded());
apply();
}
});
@@ -436,6 +440,15 @@
if (mWifiGroup == null) return;
mVpn.setVisibility(mVpnVisible ? View.VISIBLE : View.GONE);
+ if (mVpnVisible) {
+ if (mLastVpnIconId != mVpnIconId) {
+ setIconForView(mVpn, mVpnIconId);
+ mLastVpnIconId = mVpnIconId;
+ }
+ mVpn.setVisibility(View.VISIBLE);
+ } else {
+ mVpn.setVisibility(View.GONE);
+ }
if (DEBUG) Log.d(TAG, String.format("vpn: %s", mVpnVisible ? "VISIBLE" : "GONE"));
if (mEthernetVisible) {
@@ -564,6 +577,10 @@
v.setImageTintList(ColorStateList.valueOf(tint));
}
+ private int currentVpnIconId(boolean isBranded) {
+ return isBranded ? R.drawable.stat_sys_branded_vpn : R.drawable.stat_sys_vpn_ic;
+ }
+
private class PhoneState {
private final int mSubId;
private boolean mMobileVisible = false;
@@ -685,4 +702,3 @@
}
}
}
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
index 7794d5b..3e4c758 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
@@ -240,7 +240,11 @@
// Also, the notification might have been modified during the animation, so background
// might be null here.
if (iconDrawable != null) {
- iconDrawable.mutate().setColorFilter(mIconColorFilter);
+ Drawable d = iconDrawable.mutate();
+ // DrawableContainer ignores the color filter if it's already set, so clear it first to
+ // get it set and invalidated properly.
+ d.setColorFilter(null);
+ d.setColorFilter(mIconColorFilter);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 58fbd4c..b742479 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -24,7 +24,6 @@
import com.android.systemui.statusbar.policy.DataSaverController.Listener;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.HotspotController.Callback;
-import com.android.systemui.statusbar.policy.NightModeController;
/**
* Manages which tiles should be automatically added to QS.
@@ -67,35 +66,12 @@
if (!Prefs.getBoolean(context, Key.QS_WORK_ADDED, false)) {
host.getManagedProfileController().addCallback(mProfileCallback);
}
- if (!Prefs.getBoolean(context, Key.QS_NIGHT_ADDED, false)) {
- host.getNightModeController().addListener(mNightModeListener);
- }
}
public void destroy() {
// TODO: Remove any registered listeners.
}
- private final NightModeController.Listener mNightModeListener =
- new NightModeController.Listener() {
- @Override
- public void onNightModeChanged() {
- if (mHost.getNightModeController().isEnabled()) {
- mHost.addTile("night");
- Prefs.putBoolean(mContext, Key.QS_NIGHT_ADDED, true);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mHost.getNightModeController().removeListener(mNightModeListener);
- }
- });
- }
- }
-
- @Override
- public void onTwilightAutoChanged() { }
- };
-
private final ManagedProfileController.Callback mProfileCallback =
new ManagedProfileController.Callback() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
index 30d24ff..95cb672 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
@@ -14,11 +14,10 @@
package com.android.systemui.statusbar.phone;
+import android.annotation.DrawableRes;
+import android.annotation.Nullable;
import android.graphics.drawable.Drawable;
import android.view.View;
-import android.widget.ImageView;
-
-import com.android.systemui.statusbar.policy.KeyButtonView;
import java.util.ArrayList;
@@ -50,6 +49,13 @@
mViews.clear();
}
+ void addView(View view, boolean landscape) {
+ addView(view);
+ if (view instanceof ButtonInterface) {
+ ((ButtonInterface) view).setLandscape(landscape);
+ }
+ }
+
void addView(View view) {
mViews.add(view);
view.setOnClickListener(mClickListener);
@@ -65,9 +71,9 @@
view.setVisibility(mVisibility);
}
if (mImageResource > 0) {
- ((ImageView) view).setImageResource(mImageResource);
+ ((ButtonInterface) view).setImageResource(mImageResource);
} else if (mImageDrawable != null) {
- ((ImageView) view).setImageDrawable(mImageDrawable);
+ ((ButtonInterface) view).setImageDrawable(mImageDrawable);
}
}
@@ -88,7 +94,7 @@
mImageResource = -1;
final int N = mViews.size();
for (int i = 0; i < N; i++) {
- ((ImageView) mViews.get(i)).setImageDrawable(mImageDrawable);
+ ((ButtonInterface) mViews.get(i)).setImageDrawable(mImageDrawable);
}
}
@@ -97,7 +103,7 @@
mImageDrawable = null;
final int N = mViews.size();
for (int i = 0; i < N; i++) {
- ((ImageView) mViews.get(i)).setImageResource(mImageResource);
+ ((ButtonInterface) mViews.get(i)).setImageResource(mImageResource);
}
}
@@ -114,7 +120,7 @@
// This seems to be an instantaneous thing, so not going to persist it.
final int N = mViews.size();
for (int i = 0; i < N; i++) {
- ((KeyButtonView) mViews.get(i)).abortCurrentGesture();
+ ((ButtonInterface) mViews.get(i)).abortCurrentGesture();
}
}
@@ -158,6 +164,10 @@
}
}
+ public ArrayList<View> getViews() {
+ return mViews;
+ }
+
public View getCurrentView() {
return mCurrentView;
}
@@ -165,4 +175,29 @@
public void setCurrentView(View currentView) {
mCurrentView = currentView.findViewById(mId);
}
+
+ public void setCarMode(boolean carMode) {
+ final int N = mViews.size();
+ for (int i = 0; i < N; i++) {
+ final View view = mViews.get(i);
+ if (view instanceof ButtonInterface) {
+ ((ButtonInterface) view).setCarMode(carMode);
+ }
+ }
+ }
+
+ /**
+ * Interface for button actions.
+ */
+ public interface ButtonInterface {
+ void setImageResource(@DrawableRes int resId);
+
+ void setImageDrawable(@Nullable Drawable drawable);
+
+ void abortCurrentGesture();
+
+ void setLandscape(boolean landscape);
+
+ void setCarMode(boolean carMode);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 1d890d0..efceed1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -21,6 +21,7 @@
import android.text.TextUtils;
import android.util.Log;
import android.util.MathUtils;
+import android.util.SparseBooleanArray;
import com.android.systemui.R;
@@ -37,7 +38,7 @@
private final Context mContext;
- private static PulseSchedule sPulseSchedule;
+ private static IntInOutMatcher sPickupSubtypePerformsProxMatcher;
public DozeParameters(Context context) {
mContext = context;
@@ -58,10 +59,21 @@
pw.print(" getVibrateOnPickup(): "); pw.println(getVibrateOnPickup());
pw.print(" getProxCheckBeforePulse(): "); pw.println(getProxCheckBeforePulse());
pw.print(" getPulseOnNotifications(): "); pw.println(getPulseOnNotifications());
- pw.print(" getPulseSchedule(): "); pw.println(getPulseSchedule());
- pw.print(" getPulseScheduleResets(): "); pw.println(getPulseScheduleResets());
pw.print(" getPickupVibrationThreshold(): "); pw.println(getPickupVibrationThreshold());
- pw.print(" getPickupPerformsProxCheck(): "); pw.println(getPickupPerformsProxCheck());
+ pw.print(" getPickupSubtypePerformsProxCheck(): ");pw.println(
+ dumpPickupSubtypePerformsProxCheck());
+ }
+
+ private String dumpPickupSubtypePerformsProxCheck() {
+ // Refresh sPickupSubtypePerformsProxMatcher
+ getPickupSubtypePerformsProxCheck(0);
+
+ if (sPickupSubtypePerformsProxMatcher == null) {
+ return "fallback: " + mContext.getResources().getBoolean(
+ R.bool.doze_pickup_performs_proximity_check);
+ } else {
+ return "spec: " + sPickupSubtypePerformsProxMatcher.mSpec;
+ }
}
public boolean getDisplayStateSupported() {
@@ -106,26 +118,10 @@
return getBoolean("doze.pulse.proxcheck", R.bool.doze_proximity_check_before_pulse);
}
- public boolean getPickupPerformsProxCheck() {
- return getBoolean("doze.pickup.proxcheck", R.bool.doze_pickup_performs_proximity_check);
- }
-
public boolean getPulseOnNotifications() {
return getBoolean("doze.pulse.notifications", R.bool.doze_pulse_on_notifications);
}
- public PulseSchedule getPulseSchedule() {
- final String spec = getString("doze.pulse.schedule", R.string.doze_pulse_schedule);
- if (sPulseSchedule == null || !sPulseSchedule.mSpec.equals(spec)) {
- sPulseSchedule = PulseSchedule.parse(spec);
- }
- return sPulseSchedule;
- }
-
- public int getPulseScheduleResets() {
- return getInt("doze.pulse.schedule.resets", R.integer.doze_pulse_schedule_resets);
- }
-
public int getPickupVibrationThreshold() {
return getInt("doze.pickup.vibration.threshold", R.integer.doze_pickup_vibration_threshold);
}
@@ -143,43 +139,98 @@
return SystemProperties.get(propName, mContext.getString(resId));
}
- public static class PulseSchedule {
- private static final Pattern PATTERN = Pattern.compile("(\\d+?)s", 0);
+ public boolean getPickupSubtypePerformsProxCheck(int subType) {
+ String spec = getString("doze.pickup.proxcheck",
+ R.string.doze_pickup_subtype_performs_proximity_check);
- private String mSpec;
- private int[] mSchedule;
+ if (TextUtils.isEmpty(spec)) {
+ // Fall back to non-subtype based property.
+ return mContext.getResources().getBoolean(R.bool.doze_pickup_performs_proximity_check);
+ }
- public static PulseSchedule parse(String spec) {
- if (TextUtils.isEmpty(spec)) return null;
- try {
- final PulseSchedule rt = new PulseSchedule();
- rt.mSpec = spec;
- final String[] tokens = spec.split(",");
- rt.mSchedule = new int[tokens.length];
- for (int i = 0; i < tokens.length; i++) {
- final Matcher m = PATTERN.matcher(tokens[i]);
- if (!m.matches()) throw new IllegalArgumentException("Bad token: " + tokens[i]);
- rt.mSchedule[i] = Integer.parseInt(m.group(1));
+ if (sPickupSubtypePerformsProxMatcher == null
+ || !TextUtils.equals(spec, sPickupSubtypePerformsProxMatcher.mSpec)) {
+ sPickupSubtypePerformsProxMatcher = new IntInOutMatcher(spec);
+ }
+
+ return sPickupSubtypePerformsProxMatcher.isIn(subType);
+ }
+
+
+ /**
+ * Parses a spec of the form `1,2,3,!5,*`. The resulting object will match numbers that are
+ * listed, will not match numbers that are listed with a ! prefix, and will match / not match
+ * unlisted numbers depending on whether * or !* is present.
+ *
+ * * -> match any numbers that are not explicitly listed
+ * !* -> don't match any numbers that are not explicitly listed
+ * 2 -> match 2
+ * !3 -> don't match 3
+ *
+ * It is illegal to specify:
+ * - an empty spec
+ * - a spec containing that are empty, or a lone !
+ * - a spec for anything other than numbers or *
+ * - multiple terms for the same number / multiple *s
+ */
+ public static class IntInOutMatcher {
+ private static final String WILDCARD = "*";
+ private static final char OUT_PREFIX = '!';
+
+ private final SparseBooleanArray mIsIn;
+ private final boolean mDefaultIsIn;
+ final String mSpec;
+
+ public IntInOutMatcher(String spec) {
+ if (TextUtils.isEmpty(spec)) {
+ throw new IllegalArgumentException("Spec must not be empty");
+ }
+
+ boolean defaultIsIn = false;
+ boolean foundWildcard = false;
+
+ mSpec = spec;
+ mIsIn = new SparseBooleanArray();
+
+ for (String itemPrefixed : spec.split(",", -1)) {
+ if (itemPrefixed.length() == 0) {
+ throw new IllegalArgumentException(
+ "Illegal spec, must not have zero-length items: `" + spec + "`");
}
- if (DEBUG) Log.d(TAG, "Parsed spec [" + spec + "] as: " + rt);
- return rt;
- } catch (RuntimeException e) {
- Log.w(TAG, "Error parsing spec: " + spec, e);
- return null;
+ boolean isIn = itemPrefixed.charAt(0) != OUT_PREFIX;
+ String item = isIn ? itemPrefixed : itemPrefixed.substring(1);
+
+ if (itemPrefixed.length() == 0) {
+ throw new IllegalArgumentException(
+ "Illegal spec, must not have zero-length items: `" + spec + "`");
+ }
+
+ if (WILDCARD.equals(item)) {
+ if (foundWildcard) {
+ throw new IllegalArgumentException("Illegal spec, `" + WILDCARD +
+ "` must not appear multiple times in `" + spec + "`");
+ }
+ defaultIsIn = isIn;
+ foundWildcard = true;
+ } else {
+ int key = Integer.parseInt(item);
+ if (mIsIn.indexOfKey(key) >= 0) {
+ throw new IllegalArgumentException("Illegal spec, `" + key +
+ "` must not appear multiple times in `" + spec + "`");
+ }
+ mIsIn.put(key, isIn);
+ }
}
+
+ if (!foundWildcard) {
+ throw new IllegalArgumentException("Illegal spec, must specify either * or !*");
+ }
+
+ mDefaultIsIn = defaultIsIn;
}
- @Override
- public String toString() {
- return Arrays.toString(mSchedule);
- }
-
- public long getNextTime(long now, long notificationTime) {
- for (int i = 0; i < mSchedule.length; i++) {
- final long time = notificationTime + mSchedule[i] * 1000;
- if (time > now) return time;
- }
- return 0;
+ public boolean isIn(int value) {
+ return (mIsIn.get(value, mDefaultIsIn));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
index 1c9d937..2c3e805 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
@@ -20,6 +20,7 @@
import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.os.Trace;
import android.util.Log;
import com.android.keyguard.KeyguardConstants;
@@ -140,11 +141,14 @@
@Override
public void onFingerprintAcquired() {
+ Trace.beginSection("FingerprintUnlockController#onFingerprintAcquired");
releaseFingerprintWakeLock();
if (!mUpdateMonitor.isDeviceInteractive()) {
mWakeLock = mPowerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, FINGERPRINT_WAKE_LOCK_NAME);
+ Trace.beginSection("acquiring wake-and-unlock");
mWakeLock.acquire();
+ Trace.endSection();
if (DEBUG_FP_WAKELOCK) {
Log.i(TAG, "fingerprint acquired, grabbing fp wakelock");
}
@@ -159,12 +163,15 @@
mStatusBarWindowManager.setForceDozeBrightness(true);
}
}
+ Trace.endSection();
}
@Override
public void onFingerprintAuthenticated(int userId) {
+ Trace.beginSection("FingerprintUnlockController#onFingerprintAuthenticated");
if (mUpdateMonitor.isGoingToSleep()) {
mPendingAuthenticatedUserId = userId;
+ Trace.endSection();
return;
}
boolean wasDeviceInteractive = mUpdateMonitor.isDeviceInteractive();
@@ -175,25 +182,34 @@
}
mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.policy:FINGERPRINT");
}
+ Trace.beginSection("release wake-and-unlock");
releaseFingerprintWakeLock();
+ Trace.endSection();
switch (mMode) {
case MODE_DISMISS_BOUNCER:
+ Trace.beginSection("MODE_DISMISS");
mStatusBarKeyguardViewManager.notifyKeyguardAuthenticated(
false /* strongAuth */);
+ Trace.endSection();
break;
case MODE_UNLOCK:
case MODE_SHOW_BOUNCER:
+ Trace.beginSection("MODE_UNLOCK or MODE_SHOW_BOUNCER");
if (!wasDeviceInteractive) {
mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
}
mStatusBarKeyguardViewManager.animateCollapsePanels(
FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR);
+ Trace.endSection();
break;
case MODE_WAKE_AND_UNLOCK_PULSING:
+ Trace.beginSection("MODE_WAKE_AND_UNLOCK_PULSING");
mPhoneStatusBar.updateMediaMetaData(false /* metaDataChanged */,
true /* allowEnterAnimation */);
// Fall through.
+ Trace.endSection();
case MODE_WAKE_AND_UNLOCK:
+ Trace.beginSection("MODE_WAKE_AND_UNLOCK");
mStatusBarWindowManager.setStatusBarFocusable(false);
mDozeScrimController.abortPulsing();
mKeyguardViewMediator.onWakeAndUnlocking();
@@ -201,6 +217,7 @@
if (mPhoneStatusBar.getNavigationBarView() != null) {
mPhoneStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
}
+ Trace.endSection();
break;
case MODE_ONLY_WAKE:
case MODE_NONE:
@@ -210,6 +227,7 @@
mStatusBarWindowManager.setForceDozeBrightness(false);
}
mPhoneStatusBar.notifyFpAuthModeChanged();
+ Trace.endSection();
}
@Override
@@ -219,6 +237,7 @@
@Override
public void onFinishedGoingToSleep(int why) {
+ Trace.beginSection("FingerprintUnlockController#onFinishedGoingToSleep");
if (mPendingAuthenticatedUserId != -1) {
// Post this to make sure it's executed after the device is fully locked.
@@ -230,6 +249,7 @@
});
}
mPendingAuthenticatedUserId = -1;
+ Trace.endSection();
}
public int getMode() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 772c766..8cabfb9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -16,8 +16,12 @@
package com.android.systemui.statusbar.phone;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
+import android.app.ActivityOptions;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -44,6 +48,7 @@
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.TextView;
@@ -63,9 +68,6 @@
import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.PreviewInflater;
-import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
-import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-
/**
* Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
* text.
@@ -401,7 +403,7 @@
public void bindCameraPrewarmService() {
Intent intent = getCameraIntent();
ActivityInfo targetInfo = PreviewInflater.getTargetActivityInfo(mContext, intent,
- KeyguardUpdateMonitor.getCurrentUser());
+ KeyguardUpdateMonitor.getCurrentUser(), true /* onlyDirectBootAware */);
if (targetInfo != null && targetInfo.metaData != null) {
String clazz = targetInfo.metaData.getString(
MediaStore.META_DATA_STILL_IMAGE_CAMERA_PREWARM_SERVICE);
@@ -448,12 +450,24 @@
@Override
public void run() {
int result = ActivityManager.START_CANCELED;
+
+ // Normally an activity will set it's requested rotation
+ // animation on its window. However when launching an activity
+ // causes the orientation to change this is too late. In these cases
+ // the default animation is used. This doesn't look good for
+ // the camera (as it rotates the camera contents out of sync
+ // with physical reality). So, we ask the WindowManager to
+ // force the crossfade animation if an orientation change
+ // happens to occur during the launch.
+ ActivityOptions o = ActivityOptions.makeBasic();
+ o.setRotationAnimationHint(
+ WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
try {
result = ActivityManagerNative.getDefault().startActivityAsUser(
null, getContext().getBasePackageName(),
intent,
intent.resolveTypeIfNeeded(getContext().getContentResolver()),
- null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null,
+ null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, o.toBundle(),
UserHandle.CURRENT.getIdentifier());
} catch (RemoteException e) {
Log.w(TAG, "Unable to start camera activity", e);
@@ -576,10 +590,16 @@
}
private void inflateCameraPreview() {
+ View previewBefore = mCameraPreview;
+ boolean visibleBefore = false;
+ if (previewBefore != null) {
+ mPreviewContainer.removeView(previewBefore);
+ visibleBefore = previewBefore.getVisibility() == View.VISIBLE;
+ }
mCameraPreview = mPreviewInflater.inflatePreview(getCameraIntent());
if (mCameraPreview != null) {
mPreviewContainer.addView(mCameraPreview);
- mCameraPreview.setVisibility(View.INVISIBLE);
+ mCameraPreview.setVisibility(visibleBefore ? View.VISIBLE : View.INVISIBLE);
}
}
@@ -698,4 +718,9 @@
updateLeftAffordanceIcon();
updateLeftPreview();
}
+
+ public void onKeyguardShowingChanged() {
+ updateLeftAffordance();
+ inflateCameraPreview();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index ee88b00..b3e86b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -234,7 +234,6 @@
mKeyguardView.setViewMediatorCallback(mCallback);
mContainer.addView(mRoot, mContainer.getChildCount());
mRoot.setVisibility(View.INVISIBLE);
- mRoot.setSystemUiVisibility(View.STATUS_BAR_DISABLE_HOME);
}
protected void removeView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 7db2870..93ed139 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.TypedValue;
@@ -56,8 +57,10 @@
private BatteryController mBatteryController;
private KeyguardUserSwitcher mKeyguardUserSwitcher;
+ private UserSwitcherController mUserSwitcherController;
private int mSystemIconsSwitcherHiddenExpandedMargin;
+ private int mSystemIconsBaseMargin;
private View mSystemIconsContainer;
public KeyguardStatusBarView(Context context, AttributeSet attrs) {
@@ -136,8 +139,11 @@
}
private void loadDimens() {
- mSystemIconsSwitcherHiddenExpandedMargin = getResources().getDimensionPixelSize(
+ Resources res = getResources();
+ mSystemIconsSwitcherHiddenExpandedMargin = res.getDimensionPixelSize(
R.dimen.system_icons_switcher_hidden_expanded_margin);
+ mSystemIconsBaseMargin = res.getDimensionPixelSize(
+ R.dimen.system_icons_super_container_avatarless_margin_end);
}
private void updateVisibilities() {
@@ -149,13 +155,29 @@
} else if (mMultiUserSwitch.getParent() == this && mKeyguardUserSwitcherShowing) {
removeView(mMultiUserSwitch);
}
+ if (mKeyguardUserSwitcher == null) {
+ // If we have no keyguard switcher, the screen width is under 600dp. In this case,
+ // we don't show the multi-user avatar unless there is more than 1 user on the device.
+ if (mUserSwitcherController != null
+ && mUserSwitcherController.getSwitchableUserCount() > 1) {
+ mMultiUserSwitch.setVisibility(View.VISIBLE);
+ } else {
+ mMultiUserSwitch.setVisibility(View.GONE);
+ }
+ }
mBatteryLevel.setVisibility(mBatteryCharging ? View.VISIBLE : View.GONE);
}
private void updateSystemIconsLayoutParams() {
RelativeLayout.LayoutParams lp =
(LayoutParams) mSystemIconsSuperContainer.getLayoutParams();
- int marginEnd = mKeyguardUserSwitcherShowing ? mSystemIconsSwitcherHiddenExpandedMargin : 0;
+ // If the avatar icon is gone, we need to have some end margin to display the system icons
+ // correctly.
+ int baseMarginEnd = mMultiUserSwitch.getVisibility() == View.GONE
+ ? mSystemIconsBaseMargin
+ : 0;
+ int marginEnd = mKeyguardUserSwitcherShowing ? mSystemIconsSwitcherHiddenExpandedMargin :
+ baseMarginEnd;
if (marginEnd != lp.getMarginEnd()) {
lp.setMarginEnd(marginEnd);
mSystemIconsSuperContainer.setLayoutParams(lp);
@@ -187,6 +209,7 @@
}
public void setUserSwitcherController(UserSwitcherController controller) {
+ mUserSwitcherController = controller;
mMultiUserSwitch.setUserSwitcherController(controller);
}
@@ -287,6 +310,9 @@
mSystemIconsSuperContainer.animate().cancel();
mMultiUserSwitch.animate().cancel();
mMultiUserSwitch.setAlpha(1f);
+ } else {
+ updateVisibilities();
+ updateSystemIconsLayoutParams();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index ab5ee93..9bb4936 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -168,6 +168,7 @@
setContentDescription(contentDescription);
mHasFingerPrintIcon = anyFingerprintIcon;
if (animation != null && isAnim) {
+ animation.forceAnimationOnUI();
animation.start();
}
mLastState = state;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index ef19d95..2be47e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -26,6 +26,7 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
+import android.graphics.Xfermode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.DrawableWrapper;
@@ -37,6 +38,8 @@
import android.os.UserHandle;
import android.util.Log;
+import com.android.keyguard.KeyguardUpdateMonitor;
+
import libcore.io.IoUtils;
import java.util.Objects;
@@ -51,6 +54,7 @@
private final PhoneStatusBar mBar;
private final WallpaperManager mWallpaperManager;
private final Handler mH;
+ private final KeyguardUpdateMonitor mUpdateMonitor;
private boolean mCached;
private Bitmap mCache;
@@ -65,6 +69,7 @@
mH = h;
mWallpaperManager = (WallpaperManager) ctx.getSystemService(Context.WALLPAPER_SERVICE);
mCurrentUserId = ActivityManager.getCurrentUser();
+ mUpdateMonitor = KeyguardUpdateMonitor.getInstance(ctx);
IWallpaperManager service = IWallpaperManager.Stub.asInterface(
ServiceManager.getService(Context.WALLPAPER_SERVICE));
@@ -88,6 +93,7 @@
LoaderResult result = loadBitmap(mCurrentUserId, mSelectedUser);
if (result.success) {
mCached = true;
+ mUpdateMonitor.setHasLockscreenWallpaper(result.bitmap != null);
mCache = result.bitmap;
}
return mCache;
@@ -180,6 +186,7 @@
if (result.success) {
mCached = true;
mCache = result.bitmap;
+ mUpdateMonitor.setHasLockscreenWallpaper(result.bitmap != null);
mBar.updateMediaMetaData(
true /* metaDataChanged */, true /* allowEnterAnimation */);
}
@@ -224,6 +231,12 @@
}
@Override
+ public void setXfermode(@Nullable Xfermode mode) {
+ // DrawableWrapper does not call this for us.
+ getDrawable().setXfermode(mode);
+ }
+
+ @Override
public int getIntrinsicWidth() {
return -1;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index 2bee816..06c8b68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -26,6 +26,7 @@
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.Space;
+
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.KeyButtonView;
import com.android.systemui.tuner.TunerService;
@@ -69,6 +70,8 @@
private View mLastRot0;
private View mLastRot90;
+ private boolean mAlternativeOrder;
+
public NavigationBarInflaterView(Context context, AttributeSet attrs) {
super(context, attrs);
mDensity = context.getResources().getConfiguration().densityDpi;
@@ -112,6 +115,7 @@
false);
mRot90.setId(R.id.rot90);
addView(mRot90);
+ updateAlternativeOrder();
if (getParent() instanceof NavigationBarView) {
((NavigationBarView) getParent()).updateRotatedViews();
}
@@ -150,6 +154,26 @@
}
}
+ public void setAlternativeOrder(boolean alternativeOrder) {
+ if (alternativeOrder != mAlternativeOrder) {
+ mAlternativeOrder = alternativeOrder;
+ updateAlternativeOrder();
+ }
+ }
+
+ private void updateAlternativeOrder() {
+ updateAlternativeOrder(mRot0.findViewById(R.id.ends_group));
+ updateAlternativeOrder(mRot0.findViewById(R.id.center_group));
+ updateAlternativeOrder(mRot90.findViewById(R.id.ends_group));
+ updateAlternativeOrder(mRot90.findViewById(R.id.center_group));
+ }
+
+ private void updateAlternativeOrder(View v) {
+ if (v instanceof ReverseLinearLayout) {
+ ((ReverseLinearLayout) v).setAlternativeOrder(mAlternativeOrder);
+ }
+ }
+
private void initiallyFill(ButtonDispatcher buttonDispatcher) {
addAll(buttonDispatcher, (ViewGroup) mRot0.findViewById(R.id.ends_group));
addAll(buttonDispatcher, (ViewGroup) mRot0.findViewById(R.id.center_group));
@@ -256,7 +280,7 @@
params.width = (int) (params.width * size);
}
parent.addView(v);
- addToDispatchers(v);
+ addToDispatchers(v, landscape);
View lastView = landscape ? mLastRot90 : mLastRot0;
if (lastView != null) {
v.setAccessibilityTraversalAfter(lastView.getId());
@@ -303,16 +327,16 @@
return buttonSpec.substring(0, buttonSpec.indexOf(SIZE_MOD_START));
}
- private void addToDispatchers(View v) {
+ private void addToDispatchers(View v, boolean landscape) {
if (mButtonDispatchers != null) {
final int indexOfKey = mButtonDispatchers.indexOfKey(v.getId());
if (indexOfKey >= 0) {
- mButtonDispatchers.valueAt(indexOfKey).addView(v);
+ mButtonDispatchers.valueAt(indexOfKey).addView(v, landscape);
} else if (v instanceof ViewGroup) {
final ViewGroup viewGroup = (ViewGroup)v;
final int N = viewGroup.getChildCount();
for (int i = 0; i < N; i++) {
- addToDispatchers(viewGroup.getChildAt(i));
+ addToDispatchers(viewGroup.getChildAt(i), landscape);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 5fab796..45d51b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -54,7 +54,7 @@
public class NavigationBarView extends LinearLayout {
final static boolean DEBUG = false;
- final static String TAG = "PhoneStatusBar/NavigationBarView";
+ final static String TAG = "StatusBar/NavBarView";
// slippery nav bar when everything is disabled, e.g. during setup
final static boolean SLIPPERY_WHEN_DISABLED = true;
@@ -65,6 +65,7 @@
boolean mVertical;
boolean mScreenOn;
+ private int mCurrentRotation = -1;
boolean mShowMenu;
int mDisabledFlags = 0;
@@ -99,6 +100,8 @@
private final SparseArray<ButtonDispatcher> mButtonDisatchers = new SparseArray<>();
private Configuration mConfiguration;
+ private NavigationBarInflaterView mNavigationInflaterView;
+
private class NavTransitionListener implements TransitionListener {
private boolean mBackTransitioning;
private boolean mHomeAppearing;
@@ -380,10 +383,6 @@
&& ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) == 0);
final boolean disableSearch = ((disabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0);
- if (SLIPPERY_WHEN_DISABLED) {
- setSlippery(disableHome && disableRecent && disableBack && disableSearch);
- }
-
ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons);
if (navButtons != null) {
LayoutTransition lt = navButtons.getLayoutTransition();
@@ -458,22 +457,6 @@
}
}
- public void setSlippery(boolean newSlippery) {
- WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams();
- if (lp != null) {
- boolean oldSlippery = (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) != 0;
- if (!oldSlippery && newSlippery) {
- lp.flags |= WindowManager.LayoutParams.FLAG_SLIPPERY;
- } else if (oldSlippery && !newSlippery) {
- lp.flags &= ~WindowManager.LayoutParams.FLAG_SLIPPERY;
- } else {
- return;
- }
- WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE);
- wm.updateViewLayout(this, lp);
- }
- }
-
public void setMenuVisibility(final boolean show) {
setMenuVisibility(show, false);
}
@@ -492,9 +475,10 @@
@Override
public void onFinishInflate() {
+ mNavigationInflaterView = (NavigationBarInflaterView) findViewById(
+ R.id.navigation_inflater);
updateRotatedViews();
- ((NavigationBarInflaterView) findViewById(R.id.navigation_inflater)).setButtonDispatchers(
- mButtonDisatchers);
+ mNavigationInflaterView.setButtonDispatchers(mButtonDisatchers);
getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
@@ -543,6 +527,10 @@
updateCurrentView();
}
+ public boolean needsReorient(int rotation) {
+ return mCurrentRotation != rotation;
+ }
+
private void updateCurrentView() {
final int rot = mDisplay.getRotation();
for (int i=0; i<4; i++) {
@@ -550,10 +538,12 @@
}
mCurrentView = mRotatedViews[rot];
mCurrentView.setVisibility(View.VISIBLE);
+ mNavigationInflaterView.setAlternativeOrder(rot == Surface.ROTATION_90);
for (int i = 0; i < mButtonDisatchers.size(); i++) {
mButtonDisatchers.valueAt(i).setCurrentView(mCurrentView);
}
updateLayoutTransitionsEnabled();
+ mCurrentRotation = rot;
}
private void updateRecentsIcon() {
@@ -577,7 +567,7 @@
setMenuVisibility(mShowMenu, true /* force */);
if (DEBUG) {
- Log.d(TAG, "reorient(): rot=" + mDisplay.getRotation());
+ Log.d(TAG, "reorient(): rot=" + mCurrentRotation);
}
updateTaskSwitchHelper();
@@ -636,9 +626,11 @@
if (mCarMode && uiMode != Configuration.UI_MODE_TYPE_CAR) {
mCarMode = false;
uiCarModeChanged = true;
+ getHomeButton().setCarMode(mCarMode);
} else if (uiMode == Configuration.UI_MODE_TYPE_CAR) {
mCarMode = true;
uiCarModeChanged = true;
+ getHomeButton().setCarMode(mCarMode);
}
}
return uiCarModeChanged;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 204ab7e..2c8339a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -31,7 +31,6 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
-import java.util.Objects;
/**
* A class to handle notifications and their corresponding groups.
@@ -43,6 +42,7 @@
private int mBarState = -1;
private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
private HeadsUpManager mHeadsUpManager;
+ private boolean mIsUpdatingUnchangedGroup;
public void setOnGroupChangeListener(OnGroupChangeListener listener) {
mListener = listener;
@@ -140,15 +140,6 @@
}
}
- public void onEntryBundlingUpdated(final NotificationData.Entry updated,
- final String overrideGroupKey) {
- final StatusBarNotification oldSbn = updated.notification.clone();
- if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) {
- updated.notification.setOverrideGroupKey(overrideGroupKey);
- onEntryUpdated(updated, oldSbn);
- }
- }
-
private void updateSuppression(NotificationGroup group) {
if (group == null) {
return;
@@ -163,7 +154,9 @@
if (group.suppressed) {
handleSuppressedSummaryHeadsUpped(group.summary);
}
- mListener.onGroupsChanged();
+ if (!mIsUpdatingUnchangedGroup) {
+ mListener.onGroupsChanged();
+ }
}
}
@@ -192,19 +185,24 @@
public void onEntryUpdated(NotificationData.Entry entry,
StatusBarNotification oldNotification) {
+ String oldKey = oldNotification.getGroupKey();
+ String newKey = entry.notification.getGroupKey();
+ boolean groupKeysChanged = !oldKey.equals(newKey);
+ boolean wasGroupChild = isGroupChild(oldNotification);
+ boolean isGroupChild = isGroupChild(entry.notification);
+ mIsUpdatingUnchangedGroup = !groupKeysChanged && wasGroupChild == isGroupChild;
if (mGroupMap.get(getGroupKey(oldNotification)) != null) {
onEntryRemovedInternal(entry, oldNotification);
}
onEntryAdded(entry);
+ mIsUpdatingUnchangedGroup = false;
if (isIsolated(entry.notification)) {
mIsolatedEntries.put(entry.key, entry.notification);
- String oldKey = oldNotification.getGroupKey();
- String newKey = entry.notification.getGroupKey();
- if (!oldKey.equals(newKey)) {
+ if (groupKeysChanged) {
updateSuppression(mGroupMap.get(oldKey));
updateSuppression(mGroupMap.get(newKey));
}
- } else if (!isGroupChild(oldNotification) && isGroupChild(entry.notification)) {
+ } else if (!wasGroupChild && isGroupChild) {
onEntryBecomingChild(entry);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 5064d8e..f4b41bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -58,6 +58,7 @@
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -78,11 +79,10 @@
private static final int CAP_HEIGHT = 1456;
private static final int FONT_HEIGHT = 2163;
- private static final float HEADER_RUBBERBAND_FACTOR = 2.05f;
private static final float LOCK_ICON_ACTIVE_SCALE = 1.2f;
- private static final String COUNTER_PANEL_OPEN = "panel_open";
- private static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
+ static final String COUNTER_PANEL_OPEN = "panel_open";
+ static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek";
private static final Rect mDummyDirtyRect = new Rect(0, 0, 1, 1);
@@ -189,6 +189,7 @@
private boolean mExpandingFromHeadsUp;
private boolean mCollapsedOnDown;
private int mPositionMinSideMargin;
+ private int mMaxFadeoutHeight;
private int mLastOrientation = -1;
private boolean mClosingWithAlphaFadeOut;
private boolean mHeadsUpAnimatingAway;
@@ -278,6 +279,8 @@
R.dimen.qs_falsing_threshold);
mPositionMinSideMargin = getResources().getDimensionPixelSize(
R.dimen.notification_panel_min_side_margin);
+ mMaxFadeoutHeight = getResources().getDimensionPixelSize(
+ R.dimen.max_notification_fadeout_height);
}
public void updateResources() {
@@ -552,7 +555,9 @@
protected void flingToHeight(float vel, boolean expand, float target,
float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
mHeadsUpTouchHelper.notifyFling(!expand);
- setClosingWithAlphaFadeout(!expand && getFadeoutAlpha() == 1.0f);
+ setClosingWithAlphaFadeout(!expand
+ && mNotificationStackScroller.getFirstChildIntrinsicHeight() <= mMaxFadeoutHeight
+ && getFadeoutAlpha() == 1.0f);
super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
}
@@ -1013,7 +1018,7 @@
mKeyguardStatusBar.setAlpha(1f);
mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
if (keyguardShowing && oldState != mStatusBarState) {
- mKeyguardBottomArea.updateLeftAffordance();
+ mKeyguardBottomArea.onKeyguardShowingChanged();
mAfforanceHelper.updatePreviews();
}
}
@@ -1296,7 +1301,7 @@
}
}
- private void flingSettings(float vel, boolean expand) {
+ public void flingSettings(float vel, boolean expand) {
flingSettings(vel, expand, null, false /* isClick */);
}
@@ -1376,8 +1381,7 @@
int min = mStatusBarMinHeight;
if (mStatusBar.getBarState() != StatusBarState.KEYGUARD
&& mNotificationStackScroller.getNotGoneChildCount() == 0) {
- int minHeight = (int) ((mQsMinExpansionHeight + getOverExpansionAmount())
- * HEADER_RUBBERBAND_FACTOR);
+ int minHeight = (int) (mQsMinExpansionHeight + getOverExpansionAmount());
min = Math.max(min, minHeight);
}
int maxHeight;
@@ -1390,10 +1394,14 @@
return maxHeight;
}
- private boolean isInSettings() {
+ public boolean isInSettings() {
return mQsExpanded;
}
+ public boolean isExpanding() {
+ return mIsExpanding;
+ }
+
@Override
protected void onHeightUpdated(float expandedHeight) {
if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {
@@ -1548,15 +1556,8 @@
if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
return 0;
}
- if (mNotificationStackScroller.getNotGoneChildCount() == 0) {
- return Math.min(0, mExpandedHeight / HEADER_RUBBERBAND_FACTOR - mQsMinExpansionHeight);
- }
- float stackTranslation = mNotificationStackScroller.getStackTranslation();
- float translation = stackTranslation / HEADER_RUBBERBAND_FACTOR;
- if (mHeadsUpManager.hasPinnedHeadsUp() || mIsExpansionFromHeadsUp) {
- translation = mNotificationStackScroller.getTopPadding() + stackTranslation
- - mQsMinExpansionHeight;
- }
+ float translation = NotificationUtils.interpolate(-mQsMinExpansionHeight, 0,
+ mNotificationStackScroller.getAppearFraction(mExpandedHeight));
return Math.min(0, translation);
}
@@ -1616,6 +1617,9 @@
if (mQsExpanded) {
onQsExpansionStarted();
}
+ // Since there are QS tiles in the header now, we need to make sure we start listening
+ // immediately so they can be up to date.
+ mQsContainer.setHeaderListening(true);
}
@Override
@@ -1964,7 +1968,7 @@
if (mNotificationStackScroller.getNotGoneChildCount() > 0) {
return mNotificationStackScroller.getPeekHeight();
} else {
- return mQsMinExpansionHeight * HEADER_RUBBERBAND_FACTOR;
+ return mQsMinExpansionHeight;
}
}
@@ -2108,6 +2112,7 @@
onEmptySpaceClick(x);
}
+ @Override
protected boolean onMiddleClicked() {
switch (mStatusBar.getBarState()) {
case StatusBarState.KEYGUARD:
@@ -2257,6 +2262,7 @@
mStatusBar.clearNotificationEffects();
}
+ @Override
protected boolean isPanelVisibleBecauseOfHeadsUp() {
return mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index e4aa103..af85101 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -209,6 +209,9 @@
public void setTouchDisabled(boolean disabled) {
mTouchDisabled = disabled;
+ if (mTouchDisabled && mTracking) {
+ onTrackingStopped(true /* expanded */);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 323a2a5..96fb7a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -17,11 +17,25 @@
package com.android.systemui.statusbar.phone;
+import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
+import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
+import static android.app.StatusBarManager.windowStateToString;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
+import android.app.ActivityOptions;
import android.app.IActivityManager;
import android.app.Notification;
import android.app.PendingIntent;
@@ -58,6 +72,7 @@
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
+import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
@@ -69,6 +84,8 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.Trace;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.Vibrator;
@@ -76,11 +93,13 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
+import android.telecom.TelecomManager;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
import android.view.Display;
+import android.view.IRotationWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -97,6 +116,7 @@
import android.view.animation.Interpolator;
import android.widget.ImageView;
import android.widget.TextView;
+
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.NotificationVisibility;
@@ -115,7 +135,6 @@
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
-import com.android.systemui.assist.AssistManager;
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.doze.DozeHost;
@@ -170,13 +189,15 @@
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout
+ .OnChildLocationsChangedListener;
import com.android.systemui.statusbar.stack.StackStateAnimator;
import com.android.systemui.statusbar.stack.StackViewState;
import com.android.systemui.volume.VolumeComponent;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -184,19 +205,6 @@
import java.util.List;
import java.util.Map;
-import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
-import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
-import static android.app.StatusBarManager.windowStateToString;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
-
public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
HeadsUpManager.OnHeadsUpChangedListener {
@@ -360,6 +368,8 @@
private View mPendingRemoteInputView;
private View mPendingWorkRemoteInputView;
+ private View mReportRejectedTouch;
+
int mMaxAllowedKeyguardNotifications;
boolean mExpandedVisible;
@@ -765,7 +775,7 @@
// no window manager? good luck with that
}
- mAssistManager = new AssistManager(this, context);
+ mAssistManager = SystemUIFactory.getInstance().createAssistManager(this, context);
// figure out which pixel-format to use for the status bar.
mPixelFormat = PixelFormat.OPAQUE;
@@ -787,11 +797,15 @@
mBackdropFront = (ImageView) mBackdrop.findViewById(R.id.backdrop_front);
mBackdropBack = (ImageView) mBackdrop.findViewById(R.id.backdrop_back);
+ if (ENABLE_LOCKSCREEN_WALLPAPER) {
+ mLockscreenWallpaper = new LockscreenWallpaper(mContext, this, mHandler);
+ }
+
ScrimView scrimBehind = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_behind);
ScrimView scrimInFront = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_in_front);
View headsUpScrim = mStatusBarWindow.findViewById(R.id.heads_up_scrim);
mScrimController = SystemUIFactory.getInstance().createScrimController(
- scrimBehind, scrimInFront, headsUpScrim);
+ scrimBehind, scrimInFront, headsUpScrim, mLockscreenWallpaper);
if (mScrimSrcModeEnabled) {
Runnable runnable = new Runnable() {
@Override
@@ -821,10 +835,6 @@
mKeyguardBottomArea.getLockIcon());
mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController);
- if (ENABLE_LOCKSCREEN_WALLPAPER) {
- mLockscreenWallpaper = new LockscreenWallpaper(mContext, this, mHandler);
- }
-
// set the initial view visibility
setAreThereNotifications();
@@ -879,9 +889,9 @@
mLightStatusBarController = new LightStatusBarController(mIconController,
mBatteryController);
mKeyguardMonitor = new KeyguardMonitor(mContext);
+ mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor,
+ mHandler, this);
if (UserManager.get(mContext).isUserSwitcherEnabled()) {
- mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor,
- mHandler, this);
createUserSwitcher();
}
@@ -922,6 +932,36 @@
mBatteryController);
mKeyguardStatusBar.setBatteryController(mBatteryController);
+ mReportRejectedTouch = mStatusBarWindow.findViewById(R.id.report_rejected_touch);
+ if (mReportRejectedTouch != null) {
+ updateReportRejectedTouchVisibility();
+ mReportRejectedTouch.setOnClickListener(v -> {
+ Uri session = mFalsingManager.reportRejectedTouch();
+ if (session == null) { return; }
+
+ StringWriter message = new StringWriter();
+ message.write("Build info: ");
+ message.write(SystemProperties.get("ro.build.description"));
+ message.write("\nSerial number: ");
+ message.write(SystemProperties.get("ro.serialno"));
+ message.write("\n");
+
+ PrintWriter falsingPw = new PrintWriter(message);
+ FalsingLog.dump(falsingPw);
+ falsingPw.flush();
+
+ startActivityDismissingKeyguard(Intent.createChooser(new Intent(Intent.ACTION_SEND)
+ .setType("*/*")
+ .putExtra(Intent.EXTRA_SUBJECT, "Rejected touch report")
+ .putExtra(Intent.EXTRA_STREAM, session)
+ .putExtra(Intent.EXTRA_TEXT, message.toString()),
+ "Share rejected touch report")
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
+ true /* onlyProvisioned */, true /* dismissShade */);
+ });
+ }
+
+
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mBroadcastReceiver.onReceive(mContext,
new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
@@ -1176,6 +1216,7 @@
}
protected void startKeyguard() {
+ Trace.beginSection("PhoneStatusBar#startKeyguard");
KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
mFingerprintUnlockController = new FingerprintUnlockController(mContext,
mStatusBarWindowManager, mDozeScrimController, keyguardViewMediator,
@@ -1210,6 +1251,7 @@
mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
mLightStatusBarController.setFingerprintUnlockController(mFingerprintUnlockController);
+ Trace.endSection();
}
@Override
@@ -1235,6 +1277,7 @@
}
private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
+ @Override
public void onClick(View v) {
awakenDreams();
toggleRecentApps();
@@ -1299,8 +1342,29 @@
};
private final View.OnTouchListener mHomeActionListener = new View.OnTouchListener() {
+ public boolean mBlockedThisTouch;
+
+ @Override
public boolean onTouch(View v, MotionEvent event) {
+ if (mBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
+ return true;
+ }
+ // If an incoming call is ringing, HOME is totally disabled.
+ // (The user is already on the InCallUI at this point,
+ // and his ONLY options are to answer or reject the call.)
switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mBlockedThisTouch = false;
+ TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
+ if (telecomManager != null && telecomManager.isRinging()) {
+ if (mStatusBarKeyguardViewManager.isShowing()) {
+ Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " +
+ "No heads up");
+ mBlockedThisTouch = true;
+ return true;
+ }
+ }
+ break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
awakenDreams();
@@ -1345,6 +1409,27 @@
if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
if (mNavigationBarView == null) return;
+ try {
+ WindowManagerGlobal.getWindowManagerService()
+ .watchRotation(new IRotationWatcher.Stub() {
+ @Override
+ public void onRotationChanged(int rotation) throws RemoteException {
+ // We need this to be scheduled as early as possible to beat the redrawing of
+ // window in response to the orientation change.
+ Message msg = Message.obtain(mHandler, () -> {
+ if (mNavigationBarView != null
+ && mNavigationBarView.needsReorient(rotation)) {
+ repositionNavigationBar();
+ }
+ });
+ msg.setAsynchronous(true);
+ mHandler.sendMessageAtFrontOfQueue(msg);
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
prepareNavigationBarView();
mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());
@@ -1355,7 +1440,7 @@
prepareNavigationBarView();
- mWindowManager.updateViewLayout(mNavigationBarView, getNavigationBarLayoutParams());
+ mWindowManager.updateViewLayout(mNavigationBarView, mNavigationBarView.getLayoutParams());
}
private void notifyNavigationBarScreenOn(boolean screenOn) {
@@ -1372,7 +1457,8 @@
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+ | WindowManager.LayoutParams.FLAG_SLIPPERY,
PixelFormat.TRANSLUCENT);
// this will allow the navbar to run in an overlay on devices that support this
if (ActivityManager.isHighEndGfx()) {
@@ -1524,7 +1610,8 @@
}
Entry entry = mNotificationData.get(key);
- if (entry != null && mRemoteInputController.isRemoteInputActive(entry)) {
+ if (entry != null && mRemoteInputController.isRemoteInputActive(entry)
+ && (entry.row != null && !entry.row.isDismissed())) {
mLatestRankingMap = ranking;
mRemoteInputEntriesToRemoveOnCollapse.add(entry);
return;
@@ -1543,7 +1630,7 @@
&& !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) {
if (mState == StatusBarState.SHADE) {
animateCollapsePanels();
- } else if (mState == StatusBarState.SHADE_LOCKED) {
+ } else if (mState == StatusBarState.SHADE_LOCKED && !isCollapsing()) {
goToKeyguard();
}
}
@@ -2059,12 +2146,20 @@
* Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
*/
public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
- if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) return;
+ Trace.beginSection("PhoneStatusBar#updateMediaMetaData");
+ if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) {
+ Trace.endSection();
+ return;
+ }
- if (mBackdrop == null) return; // called too early
+ if (mBackdrop == null) {
+ Trace.endSection();
+ return; // called too early
+ }
if (mLaunchTransitionFadingAway) {
mBackdrop.setVisibility(View.INVISIBLE);
+ Trace.endSection();
return;
}
@@ -2217,6 +2312,15 @@
}
}
}
+ Trace.endSection();
+ }
+
+ private void updateReportRejectedTouchVisibility() {
+ if (mReportRejectedTouch == null) {
+ return;
+ }
+ mReportRejectedTouch.setVisibility(mState == StatusBarState.KEYGUARD
+ && mFalsingManager.isReportingEnabled() ? View.VISIBLE : View.INVISIBLE);
}
protected int adjustDisableFlags(int state) {
@@ -2231,6 +2335,7 @@
/**
* State is one or more of the DISABLE constants from StatusBarManager.
*/
+ @Override
public void disable(int state1, int state2, boolean animate) {
animate &= mStatusBarWindowState != WINDOW_STATE_HIDDEN;
mDisabledUnmodified1 = state1;
@@ -2431,6 +2536,7 @@
mStatusBarWindowManager.setHeadsUpShowing(false);
mHeadsUpManager.setHeadsUpGoingAway(false);
}
+ removeRemoteInputEntriesKeptUntilCollapsed();
}
});
}
@@ -2460,6 +2566,7 @@
}
+ @Override
protected void updateHeadsUp(String key, Entry entry, boolean shouldPeek,
boolean alertAgain) {
final boolean wasHeadsUp = isHeadsUp(key);
@@ -2476,6 +2583,7 @@
}
}
+ @Override
protected void setHeadsUpUser(int newUserId) {
if (mHeadsUpManager != null) {
mHeadsUpManager.setUser(newUserId);
@@ -2486,6 +2594,7 @@
return mHeadsUpManager.isHeadsUp(key);
}
+ @Override
protected boolean isSnoozedPackage(StatusBarNotification sbn) {
return mHeadsUpManager.isSnoozed(sbn.getPackageName());
}
@@ -2526,6 +2635,7 @@
* All changes to the status bar and notifications funnel through here and are batched.
*/
private class H extends BaseStatusBar.H {
+ @Override
public void handleMessage(Message m) {
super.handleMessage(m);
switch (m.what) {
@@ -2567,6 +2677,37 @@
mHeadsUpManager.releaseAllImmediately();
}
+ /**
+ * Called for system navigation gestures. First action opens the panel, second opens
+ * settings. Down action closes the entire panel.
+ */
+ @Override
+ public void handleSystemNavigationKey(int key) {
+ if (SPEW) Log.d(TAG, "handleSystemNavigationKey: " + key);
+ if (!panelsEnabled() || !mKeyguardMonitor.isDeviceInteractive()
+ || mKeyguardMonitor.isShowing()) {
+ return;
+ }
+
+ // Panels are not available in setup
+ if (!mUserSetup) return;
+
+ if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP == key) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_UP);
+ mNotificationPanel.collapse(false /* delayed */, 1.0f /* speedUpFactor */);
+ } else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN);
+ if (mNotificationPanel.isFullyCollapsed()) {
+ mNotificationPanel.expand(true /* animate */);
+ MetricsLogger.count(mContext, NotificationPanelView.COUNTER_PANEL_OPEN, 1);
+ } else if (!mNotificationPanel.isInSettings() && !mNotificationPanel.isExpanding()){
+ mNotificationPanel.flingSettings(0 /* velocity */, true /* expand */);
+ MetricsLogger.count(mContext, NotificationPanelView.COUNTER_PANEL_OPEN_QS, 1);
+ }
+ }
+
+ }
+
boolean panelsEnabled() {
return (mDisabled1 & StatusBarManager.DISABLE_EXPAND) == 0 && !ONLY_CORE_APPS;
}
@@ -2578,8 +2719,6 @@
}
mExpandedVisible = true;
- if (mNavigationBarView != null)
- mNavigationBarView.setSlippery(true);
// Expand the window to encompass the full screen in anticipation of the drag.
// This is only possible to do atomically because the status bar is at the top of the screen!
@@ -2610,15 +2749,18 @@
mHandler.sendEmptyMessage(MSG_OPEN_SETTINGS_PANEL);
}
+ @Override
public void animateCollapsePanels(int flags) {
animateCollapsePanels(flags, false /* force */, false /* delayed */,
1.0f /* speedUpFactor */);
}
+ @Override
public void animateCollapsePanels(int flags, boolean force) {
animateCollapsePanels(flags, force, false /* delayed */, 1.0f /* speedUpFactor */);
}
+ @Override
public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
animateCollapsePanels(flags, force, delayed, 1.0f /* speedUpFactor */);
}
@@ -2713,8 +2855,7 @@
mNotificationPanel.closeQs();
mExpandedVisible = false;
- if (mNavigationBarView != null)
- mNavigationBarView.setSlippery(false);
+
visibilityChanged(false);
// Shrink the window to the size of the status bar only
@@ -3056,6 +3197,7 @@
}
}
+ @Override
public void topAppWindowChanged(boolean showMenu) {
if (SPEW) {
Log.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
@@ -3092,6 +3234,7 @@
+ ") " + v.getWidth() + "x" + v.getHeight() + "]";
}
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
synchronized (mQueueLock) {
pw.println("Current Status Bar state:");
@@ -3169,6 +3312,7 @@
pw.println("see the logcat for a dump of the views we have created.");
// must happen on ui thread
mHandler.post(new Runnable() {
+ @Override
public void run() {
mStatusBarView.getLocationOnScreen(mAbsPos);
Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
@@ -3279,18 +3423,32 @@
mContext, intent, mCurrentUserId);
final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
Runnable runnable = new Runnable() {
+ @Override
public void run() {
mAssistManager.hideAssist();
intent.setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
int result = ActivityManager.START_CANCELED;
+ ActivityOptions options = new ActivityOptions(getActivityOptions());
+ if (intent == KeyguardBottomAreaView.INSECURE_CAMERA_INTENT) {
+ // Normally an activity will set it's requested rotation
+ // animation on its window. However when launching an activity
+ // causes the orientation to change this is too late. In these cases
+ // the default animation is used. This doesn't look good for
+ // the camera (as it rotates the camera contents out of sync
+ // with physical reality). So, we ask the WindowManager to
+ // force the crossfade animation if an orientation change
+ // happens to occur during the launch.
+ options.setRotationAnimationHint(
+ WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
+ }
try {
result = ActivityManagerNative.getDefault().startActivityAsUser(
null, mContext.getBasePackageName(),
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
- getActivityOptions(), UserHandle.CURRENT.getIdentifier());
+ options.toBundle(), UserHandle.CURRENT.getIdentifier());
} catch (RemoteException e) {
Log.w(TAG, "Unable to start activity", e);
}
@@ -3323,6 +3481,7 @@
@Override
public boolean onDismiss() {
AsyncTask.execute(new Runnable() {
+ @Override
public void run() {
try {
if (keyguardShowing && !afterKeyguardGone) {
@@ -3346,6 +3505,7 @@
}
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) Log.v(TAG, "onReceive: " + intent);
String action = intent.getAction();
@@ -3376,6 +3536,7 @@
};
private BroadcastReceiver mDemoReceiver = new BroadcastReceiver() {
+ @Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) Log.v(TAG, "onReceive: " + intent);
String action = intent.getAction();
@@ -3457,6 +3618,7 @@
setControllerUsers();
clearCurrentMediaNotification();
mLockscreenWallpaper.setCurrentUser(newUserId);
+ mScrimController.setCurrentUser(newUserId);
updateMediaMetaData(true, false);
}
@@ -3634,6 +3796,7 @@
}
Runnable mStartTracing = new Runnable() {
+ @Override
public void run() {
vibrate();
SystemClock.sleep(250);
@@ -3644,6 +3807,7 @@
};
Runnable mStopTracing = new Runnable() {
+ @Override
public void run() {
android.os.Debug.stopMethodTracing();
Log.d(TAG, "stopTracing");
@@ -3987,6 +4151,7 @@
* @return true if we would like to stay in the shade, false if it should go away entirely
*/
public boolean hideKeyguard() {
+ Trace.beginSection("PhoneStatusBar#hideKeyguard");
boolean staying = mLeaveOpenOnKeyguardHide;
setBarState(StatusBarState.SHADE);
View viewToClick = null;
@@ -4031,6 +4196,7 @@
mNotificationPanel.onAffordanceLaunchEnded();
mNotificationPanel.animate().cancel();
mNotificationPanel.setAlpha(1f);
+ Trace.endSection();
return staying;
}
@@ -4105,6 +4271,7 @@
}
protected void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked) {
+ Trace.beginSection("PhoneStatusBar#updateKeyguardState");
if (mState == StatusBarState.KEYGUARD) {
mKeyguardIndicationController.setVisible(true);
mNotificationPanel.resetViews();
@@ -4136,9 +4303,11 @@
updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
mStatusBarKeyguardViewManager.isSecure());
+ Trace.endSection();
}
private void updateDozingState() {
+ Trace.beginSection("PhoneStatusBar#updateDozingState");
boolean animate = !mDozing && mDozeScrimController.isPulsing();
mNotificationPanel.setDozing(mDozing, animate);
mStackScroller.setDark(mDozing, animate, mWakeUpTouchLocation);
@@ -4149,6 +4318,7 @@
mDozeScrimController.setDozing(mDozing &&
mFingerprintUnlockController.getMode()
!= FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING, animate);
+ Trace.endSection();
}
public void updateStackScrollerState(boolean goingToFullShade, boolean fromShadeLocked) {
@@ -4249,6 +4419,10 @@
mStackScroller.setActivatedChild(view);
}
+ public ButtonDispatcher getHomeButton() {
+ return mNavigationBarView.getHomeButton();
+ }
+
/**
* @param state The {@link StatusBarState} to set.
*/
@@ -4268,6 +4442,7 @@
mGroupManager.setStatusBarState(state);
mFalsingManager.setStatusBarState(state);
mStatusBarWindowManager.setStatusBarState(state);
+ updateReportRejectedTouchVisibility();
updateDozing();
}
@@ -4596,7 +4771,7 @@
private void vibrateForCameraGesture() {
// Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep.
- mVibrator.vibrate(new long[]{0, 750L}, -1 /* repeat */);
+ mVibrator.vibrate(new long[]{0, 400}, -1 /* repeat */);
}
public void onScreenTurnedOn() {
@@ -4746,11 +4921,13 @@
}
private void updateDozing() {
+ Trace.beginSection("PhoneStatusBar#updateDozing");
// When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
mDozing = mDozingRequested && mState == StatusBarState.KEYGUARD
|| mFingerprintUnlockController.getMode()
== FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
updateDozingState();
+ Trace.endSection();
}
private final class ShadeUpdates {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index d9dd9e2..a13138d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -144,7 +144,7 @@
// listen for user / profile change.
try {
- ActivityManagerNative.getDefault().registerUserSwitchObserver(mUserSwitchListener);
+ ActivityManagerNative.getDefault().registerUserSwitchObserver(mUserSwitchListener, TAG);
} catch (RemoteException e) {
// Ignore
}
@@ -217,6 +217,8 @@
mSimState = IccCardConstants.State.ABSENT;
} else if (IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(stateExtra)) {
mSimState = IccCardConstants.State.CARD_IO_ERROR;
+ } else if (IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED.equals(stateExtra)) {
+ mSimState = IccCardConstants.State.CARD_RESTRICTED;
} else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
mSimState = IccCardConstants.State.READY;
} else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index fa57775..da698d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -27,6 +27,7 @@
import android.os.Looper;
import android.os.Process;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.service.quicksettings.Tile;
@@ -51,6 +52,7 @@
import com.android.systemui.qs.tiles.HotspotTile;
import com.android.systemui.qs.tiles.IntentTile;
import com.android.systemui.qs.tiles.LocationTile;
+import com.android.systemui.qs.tiles.NightDisplayTile;
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.qs.tiles.UserTile;
import com.android.systemui.qs.tiles.WifiTile;
@@ -59,7 +61,6 @@
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NightModeController;
import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -70,7 +71,6 @@
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.tuner.NightModeTile;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -86,7 +86,7 @@
private static final String TAG = "QSTileHost";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- public static final String TILES_SETTING = "sysui_qs_tiles";
+ public static final String TILES_SETTING = Secure.QS_TILES;
private final Context mContext;
private final PhoneStatusBar mStatusBar;
@@ -110,7 +110,6 @@
private final TileServices mServices;
private final List<Callback> mCallbacks = new ArrayList<>();
- private final NightModeController mNightModeController;
private final AutoTileManager mAutoTiles;
private final ManagedProfileController mProfileController;
private final NextAlarmController mNextAlarmController;
@@ -143,7 +142,6 @@
mBattery = battery;
mIconController = iconController;
mNextAlarmController = nextAlarmController;
- mNightModeController = new NightModeController(mContext, true);
mProfileController = new ManagedProfileController(this);
final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName(),
@@ -307,10 +305,6 @@
return mIconController;
}
- public NightModeController getNightModeController() {
- return mNightModeController;
- }
-
public ManagedProfileController getManagedProfileController() {
return mProfileController;
}
@@ -321,6 +315,9 @@
return;
}
if (DEBUG) Log.d(TAG, "Recreating tiles");
+ if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
+ newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode);
+ }
final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
int currentUser = ActivityManager.getCurrentUser();
if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
@@ -337,6 +334,9 @@
|| ((CustomTile) tile).getUser() == currentUser)) {
if (DEBUG) Log.d(TAG, "Adding " + tile);
tile.removeCallbacks();
+ if (!(tile instanceof CustomTile) && mCurrentUser != currentUser) {
+ tile.userSwitch(currentUser);
+ }
newTiles.put(tileSpec, tile);
} else {
if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec);
@@ -403,23 +403,11 @@
ComponentName component = CustomTile.getComponentFromSpec(tileSpec);
Intent intent = new Intent().setComponent(component);
TileLifecycleManager lifecycleManager = new TileLifecycleManager(new Handler(),
- mContext, mServices, new Tile(component), intent,
+ mContext, mServices, new Tile(), intent,
new UserHandle(ActivityManager.getCurrentUser()));
lifecycleManager.onStopListening();
lifecycleManager.onTileRemoved();
- lifecycleManager.flushMessagesAndUnbind();
- }
- }
- for (int i = 0; i < NA; i++) {
- String tileSpec = newTiles.get(i);
- if (!tileSpec.startsWith(CustomTile.PREFIX)) continue;
- if (!previousTiles.contains(tileSpec)) {
- ComponentName component = CustomTile.getComponentFromSpec(tileSpec);
- Intent intent = new Intent().setComponent(component);
- TileLifecycleManager lifecycleManager = new TileLifecycleManager(new Handler(),
- mContext, mServices, new Tile(component), intent,
- new UserHandle(ActivityManager.getCurrentUser()));
- lifecycleManager.onTileAdded();
+ TileLifecycleManager.setTileAdded(mContext, component, false);
lifecycleManager.flushMessagesAndUnbind();
}
}
@@ -444,8 +432,7 @@
else if (tileSpec.equals("user")) return new UserTile(this);
else if (tileSpec.equals("battery")) return new BatteryTile(this);
else if (tileSpec.equals("saver")) return new DataSaverTile(this);
- else if (tileSpec.equals(NightModeTile.NIGHT_MODE_SPEC))
- return new NightModeTile(this);
+ else if (tileSpec.equals("night")) return new NightDisplayTile(this);
// Intent tiles.
else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec);
else if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(this,tileSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index 2de8329..7a2ae22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -23,6 +23,7 @@
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
+import android.os.UserManager;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
@@ -36,11 +37,11 @@
import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
-import com.android.systemui.qs.QSAnimator;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSPanel.Callback;
import com.android.systemui.qs.QuickQSPanel;
import com.android.systemui.qs.TouchAnimator;
+import com.android.systemui.qs.TouchAnimator.Builder;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
@@ -82,19 +83,14 @@
protected MultiUserSwitch mMultiUserSwitch;
private ImageView mMultiUserAvatar;
- private float mDateTimeTranslation;
- private float mDateTimeAlarmTranslation;
- private float mDateScaleFactor;
- protected float mGearTranslation;
- private TouchAnimator mSecondHalfAnimator;
- private TouchAnimator mFirstHalfAnimator;
- private TouchAnimator mDateSizeAnimator;
- private TouchAnimator mAlarmTranslation;
+ private TouchAnimator mAnimator;
protected TouchAnimator mSettingsAlpha;
private float mExpansionAmount;
private QSTileHost mHost;
+ private View mEdit;
private boolean mShowFullAlarm;
+ private float mDateTimeTranslation;
public QuickStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -106,11 +102,16 @@
mEmergencyOnly = (TextView) findViewById(R.id.header_emergency_calls_only);
+ mEdit = findViewById(android.R.id.edit);
+ findViewById(android.R.id.edit).setOnClickListener(view ->
+ mHost.startRunnableDismissingKeyguard(() -> mQsPanel.showEdit(view)));
+
mDateTimeAlarmGroup = (ViewGroup) findViewById(R.id.date_time_alarm_group);
mDateTimeAlarmGroup.findViewById(R.id.empty_time_view).setVisibility(View.GONE);
mDateTimeGroup = (ViewGroup) findViewById(R.id.date_time_group);
mDateTimeGroup.setPivotX(0);
mDateTimeGroup.setPivotY(0);
+ mDateTimeTranslation = getResources().getDimension(R.dimen.qs_date_time_translation);
mShowFullAlarm = getResources().getBoolean(R.bool.quick_settings_show_full_alarm);
mExpandIndicator = (ExpandableIndicator) findViewById(R.id.expand_indicator);
@@ -152,47 +153,21 @@
FontSizeUtils.updateFontSize(mAlarmStatus, R.dimen.qs_date_collapsed_size);
FontSizeUtils.updateFontSize(mEmergencyOnly, R.dimen.qs_emergency_calls_only_text_size);
- mGearTranslation = mContext.getResources().getDimension(R.dimen.qs_header_gear_translation);
-
- mDateTimeTranslation = mContext.getResources().getDimension(
- R.dimen.qs_date_anim_translation);
- mDateTimeAlarmTranslation = mContext.getResources().getDimension(
- R.dimen.qs_date_alarm_anim_translation);
- float dateCollapsedSize = mContext.getResources().getDimension(
- R.dimen.qs_date_collapsed_text_size);
- float dateExpandedSize = mContext.getResources().getDimension(
- R.dimen.qs_date_text_size);
- mDateScaleFactor = dateExpandedSize / dateCollapsedSize;
- updateDateTimePosition();
-
- mSecondHalfAnimator = new TouchAnimator.Builder()
+ Builder builder = new Builder()
.addFloat(mShowFullAlarm ? mAlarmStatus : findViewById(R.id.date), "alpha", 0, 1)
- .addFloat(mEmergencyOnly, "alpha", 0, 1)
- .setStartDelay(.5f)
- .build();
+ .addFloat(mEmergencyOnly, "alpha", 0, 1);
if (mShowFullAlarm) {
- mFirstHalfAnimator = new TouchAnimator.Builder()
- .addFloat(mAlarmStatusCollapsed, "alpha", 1, 0)
- .setEndDelay(.5f)
- .build();
+ builder.addFloat(mAlarmStatusCollapsed, "alpha", 1, 0);
}
- mDateSizeAnimator = new TouchAnimator.Builder()
- .addFloat(mDateTimeGroup, "scaleX", 1, mDateScaleFactor)
- .addFloat(mDateTimeGroup, "scaleY", 1, mDateScaleFactor)
- .setStartDelay(.36f)
- .build();
+ mAnimator = builder.build();
updateSettingsAnimator();
}
protected void updateSettingsAnimator() {
mSettingsAlpha = new TouchAnimator.Builder()
- .addFloat(mSettingsContainer, "translationY", -mGearTranslation, 0)
- .addFloat(mMultiUserSwitch, "translationY", -mGearTranslation, 0)
- .addFloat(mSettingsButton, "rotation", -90, 0)
- .addFloat(mSettingsContainer, "alpha", 0, 1)
+ .addFloat(mEdit, "alpha", 0, 1)
.addFloat(mMultiUserSwitch, "alpha", 0, 1)
- .setStartDelay(QSAnimator.EXPANDED_TILE_DELAY)
.build();
final boolean isRtl = isLayoutRtl();
@@ -222,6 +197,7 @@
@Override
public void setExpanded(boolean expanded) {
+ if (mExpanded == expanded) return;
mExpanded = expanded;
mHeaderQsPanel.setExpanded(expanded);
updateEverything();
@@ -247,12 +223,8 @@
@Override
public void setExpansion(float headerExpansionFraction) {
mExpansionAmount = headerExpansionFraction;
- mSecondHalfAnimator.setPosition(headerExpansionFraction);
- if (mShowFullAlarm) {
- mFirstHalfAnimator.setPosition(headerExpansionFraction);
- }
- mDateSizeAnimator.setPosition(headerExpansionFraction);
- mAlarmTranslation.setPosition(headerExpansionFraction);
+ updateDateTimePosition();
+ mAnimator.setPosition(headerExpansionFraction);
mSettingsAlpha.setPosition(headerExpansionFraction);
updateAlarmVisibilities();
@@ -273,15 +245,6 @@
mAlarmStatusCollapsed.setVisibility(mAlarmShowing ? View.VISIBLE : View.INVISIBLE);
}
- private void updateDateTimePosition() {
- // This one has its own because we have to rebuild it every time the alarm state changes.
- mAlarmTranslation = new TouchAnimator.Builder()
- .addFloat(mDateTimeAlarmGroup, "translationY", 0, mAlarmShowing
- ? mDateTimeAlarmTranslation : mDateTimeTranslation)
- .build();
- mAlarmTranslation.setPosition(mExpansionAmount);
- }
-
public void setListening(boolean listening) {
if (listening == mListening) {
return;
@@ -293,20 +256,28 @@
@Override
public void updateEverything() {
- updateDateTimePosition();
- updateVisibilities();
- setClickable(false);
+ post(() -> {
+ updateVisibilities();
+ setClickable(false);
+ });
}
protected void updateVisibilities() {
updateAlarmVisibilities();
+ updateDateTimePosition();
mEmergencyOnly.setVisibility(mExpanded && mShowEmergencyCallsOnly
? View.VISIBLE : View.INVISIBLE);
- mSettingsContainer.setVisibility(mExpanded ? View.VISIBLE : View.INVISIBLE);
mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
- mMultiUserSwitch.setVisibility(mExpanded && mMultiUserSwitch.hasMultipleUsers()
+ final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
+ mMultiUserSwitch.setVisibility(mExpanded && mMultiUserSwitch.hasMultipleUsers() && !isDemo
? View.VISIBLE : View.INVISIBLE);
+ mEdit.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
+ }
+
+ private void updateDateTimePosition() {
+ mDateTimeAlarmGroup.setTranslationY(mShowEmergencyCallsOnly
+ ? mExpansionAmount * mDateTimeTranslation : 0);
}
private void updateListeners() {
@@ -350,7 +321,8 @@
public void onClick(View v) {
if (v == mSettingsButton) {
MetricsLogger.action(mContext,
- MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH);
+ mExpanded ? MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
+ : MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH);
if (mSettingsButton.isTunerClick()) {
mHost.startRunnableDismissingKeyguard(() -> post(() -> {
if (TunerService.isTunerEnabled(mContext)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java
index 3682aa1..f45967a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java
@@ -30,7 +30,11 @@
*/
public class ReverseLinearLayout extends LinearLayout {
- private boolean mIsLayoutRtl;
+ /** If true, the layout is reversed vs. a regular linear layout */
+ private boolean mIsLayoutReverse;
+
+ /** If true, the layout is opposite to it's natural reversity from the layout direction */
+ private boolean mIsAlternativeOrder;
public ReverseLinearLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
@@ -39,45 +43,50 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mIsLayoutRtl = getResources().getConfiguration()
- .getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+ updateOrder();
}
@Override
public void addView(View child) {
reversParams(child.getLayoutParams());
- if (mIsLayoutRtl) {
- super.addView(child);
- } else {
+ if (mIsLayoutReverse) {
super.addView(child, 0);
+ } else {
+ super.addView(child);
}
}
@Override
public void addView(View child, ViewGroup.LayoutParams params) {
reversParams(params);
- if (mIsLayoutRtl) {
- super.addView(child, params);
- } else {
+ if (mIsLayoutReverse) {
super.addView(child, 0, params);
+ } else {
+ super.addView(child, params);
}
}
@Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- updateRTLOrder();
+ public void onRtlPropertiesChanged(int layoutDirection) {
+ super.onRtlPropertiesChanged(layoutDirection);
+ updateOrder();
+ }
+
+ public void setAlternativeOrder(boolean alternative) {
+ mIsAlternativeOrder = alternative;
+ updateOrder();
}
/**
* In landscape, the LinearLayout is not auto mirrored since it is vertical. Therefore we
* have to do it manually
*/
- private void updateRTLOrder() {
- boolean isLayoutRtl = getResources().getConfiguration()
- .getLayoutDirection() == LAYOUT_DIRECTION_RTL;
- if (mIsLayoutRtl != isLayoutRtl) {
- // RTL changed, swap the order of all views.
+ private void updateOrder() {
+ boolean isLayoutRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+ boolean isLayoutReverse = isLayoutRtl ^ mIsAlternativeOrder;
+
+ if (mIsLayoutReverse != isLayoutReverse) {
+ // reversity changed, swap the order of all views.
int childCount = getChildCount();
ArrayList<View> childList = new ArrayList<>(childCount);
for (int i = 0; i < childCount; i++) {
@@ -87,7 +96,7 @@
for (int i = childCount - 1; i >= 0; i--) {
super.addView(childList.get(i));
}
- mIsLayoutRtl = isLayoutRtl;
+ mIsLayoutReverse = isLayoutReverse;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 9c4480e..8b87a7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -30,6 +30,7 @@
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationData;
@@ -46,10 +47,13 @@
public static final long ANIMATION_DURATION = 220;
public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR
= new PathInterpolator(0f, 0, 0.7f, 1f);
+ public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR_LOCKED
+ = new PathInterpolator(0.3f, 0f, 0.8f, 1f);
private static final float SCRIM_BEHIND_ALPHA = 0.62f;
- private static final float SCRIM_BEHIND_ALPHA_KEYGUARD = 0.45f;
- private static final float SCRIM_BEHIND_ALPHA_UNLOCKING = 0.2f;
+ protected static final float SCRIM_BEHIND_ALPHA_KEYGUARD = 0.45f;
+ protected static final float SCRIM_BEHIND_ALPHA_UNLOCKING = 0.2f;
private static final float SCRIM_IN_FRONT_ALPHA = 0.75f;
+ private static final float SCRIM_IN_FRONT_ALPHA_LOCKED = 0.85f;
private static final int TAG_KEY_ANIM = R.id.scrim;
private static final int TAG_KEY_ANIM_TARGET = R.id.scrim_target;
private static final int TAG_START_ALPHA = R.id.scrim_alpha_start;
@@ -59,6 +63,7 @@
private final ScrimView mScrimInFront;
private final UnlockMethodCache mUnlockMethodCache;
private final View mHeadsUpScrim;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private float mScrimBehindAlpha = SCRIM_BEHIND_ALPHA;
private float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD;
@@ -99,6 +104,7 @@
mHeadsUpScrim = headsUpScrim;
final Context context = scrimBehind.getContext();
mUnlockMethodCache = UnlockMethodCache.getInstance(context);
+ mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
updateHeadsUpScrim(false);
}
@@ -120,6 +126,13 @@
scheduleUpdate();
}
+ protected void setScrimBehindValues(float scrimBehindAlphaKeyguard,
+ float scrimBehindAlphaUnlocking) {
+ mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard;
+ mScrimBehindAlphaUnlocking = scrimBehindAlphaUnlocking;
+ scheduleUpdate();
+ }
+
public void onTrackingStarted() {
mExpanding = true;
mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer();
@@ -162,11 +175,19 @@
mAnimateChange = true;
mSkipFirstFrame = skipFirstFrame;
mOnAnimationFinished = onAnimationFinished;
- scheduleUpdate();
- // No need to wait for the next frame to be drawn for this case - onPreDraw will execute
- // the changes we just scheduled.
- onPreDraw();
+ if (mKeyguardUpdateMonitor.isUserUnlocked()) {
+ scheduleUpdate();
+
+ // No need to wait for the next frame to be drawn for this case - onPreDraw will execute
+ // the changes we just scheduled.
+ onPreDraw();
+ } else {
+
+ // In case the user isn't unlocked, make sure to delay a bit because the system is hosed
+ // with too many things in this case, in order to not skip the initial frames.
+ mScrimInFront.postOnAnimationDelayed(this::scheduleUpdate, 16);
+ }
}
public void abortKeyguardFadingOut() {
@@ -211,6 +232,11 @@
return mDozeInFrontAlpha;
}
+ private float getScrimInFrontAlpha() {
+ return mKeyguardUpdateMonitor.isUserUnlocked()
+ ? SCRIM_IN_FRONT_ALPHA
+ : SCRIM_IN_FRONT_ALPHA_LOCKED;
+ }
private void scheduleUpdate() {
if (mUpdatePending) return;
@@ -250,10 +276,10 @@
float fraction = 1 - behindFraction;
fraction = (float) Math.pow(fraction, 0.8f);
behindFraction = (float) Math.pow(behindFraction, 0.8f);
- setScrimInFrontColor(fraction * SCRIM_IN_FRONT_ALPHA);
+ setScrimInFrontColor(fraction * getScrimInFrontAlpha());
setScrimBehindColor(behindFraction * mScrimBehindAlphaKeyguard);
} else if (mBouncerShowing) {
- setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA);
+ setScrimInFrontColor(getScrimInFrontAlpha());
setScrimBehindColor(0f);
} else {
float fraction = Math.max(0, Math.min(mFraction, 1));
@@ -371,7 +397,13 @@
}
private Interpolator getInterpolator() {
- return mAnimateKeyguardFadingOut ? KEYGUARD_FADE_OUT_INTERPOLATOR : mInterpolator;
+ if (mAnimateKeyguardFadingOut && !mKeyguardUpdateMonitor.isUserUnlocked()) {
+ return KEYGUARD_FADE_OUT_INTERPOLATOR_LOCKED;
+ } else if (mAnimateKeyguardFadingOut) {
+ return KEYGUARD_FADE_OUT_INTERPOLATOR;
+ } else {
+ return mInterpolator;
+ }
}
@Override
@@ -524,6 +556,10 @@
mScrimBehind.setExcludedArea(area);
}
+ public void setLeftInset(int inset) {
+ mScrimBehind.setLeftInset(inset);
+ }
+
public int getScrimBehindColor() {
return mScrimBehind.getScrimColorWithAlpha();
}
@@ -538,4 +574,8 @@
R.dimen.heads_up_scrim_height);
mHeadsUpScrim.setLayoutParams(layoutParams);
}
+
+ public void setCurrentUser(int currentUser) {
+ // Don't care in the base class.
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
deleted file mode 100644
index 72eafd8..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ /dev/null
@@ -1,842 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Outline;
-import android.graphics.Rect;
-import android.graphics.drawable.Animatable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.RippleDrawable;
-import android.util.AttributeSet;
-import android.util.MathUtils;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
-import android.widget.Switch;
-import android.widget.TextView;
-import android.widget.Toast;
-import com.android.keyguard.KeyguardStatusView;
-import com.android.systemui.BatteryMeterView;
-import com.android.systemui.FontSizeUtils;
-import com.android.systemui.R;
-import com.android.systemui.qs.QSPanel;
-import com.android.systemui.qs.QSPanel.Callback;
-import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTile.DetailAdapter;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener;
-import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.tuner.TunerService;
-
-import java.text.NumberFormat;
-
-/**
- * The view to manage the header area in the expanded status bar.
- */
-public class StatusBarHeaderView extends BaseStatusBarHeader implements View.OnClickListener,
- BatteryController.BatteryStateChangeCallback, NextAlarmController.NextAlarmChangeCallback,
- EmergencyListener {
-
- private boolean mExpanded;
- private boolean mListening;
-
- private ViewGroup mSystemIconsContainer;
- private View mSystemIconsSuperContainer;
- private View mDateGroup;
- private View mClock;
- private TextView mTime;
- private TextView mAmPm;
- private MultiUserSwitch mMultiUserSwitch;
- private ImageView mMultiUserAvatar;
- private TextView mDateCollapsed;
- private TextView mDateExpanded;
- private LinearLayout mSystemIcons;
- private View mSignalCluster;
- private SettingsButton mSettingsButton;
- private View mSettingsContainer;
- private View mQsDetailHeader;
- private TextView mQsDetailHeaderTitle;
- private Switch mQsDetailHeaderSwitch;
- private ImageView mQsDetailHeaderProgress;
- private TextView mEmergencyCallsOnly;
- private TextView mBatteryLevel;
- private TextView mAlarmStatus;
-
- private boolean mShowEmergencyCallsOnly;
- private boolean mAlarmShowing;
- private AlarmManager.AlarmClockInfo mNextAlarm;
-
- private int mCollapsedHeight;
- private int mExpandedHeight;
-
- private int mMultiUserExpandedMargin;
- private int mMultiUserCollapsedMargin;
-
- private int mClockMarginBottomExpanded;
- private int mClockMarginBottomCollapsed;
- private int mMultiUserSwitchWidthCollapsed;
- private int mMultiUserSwitchWidthExpanded;
-
- private int mClockCollapsedSize;
- private int mClockExpandedSize;
-
- /**
- * In collapsed QS, the clock and avatar are scaled down a bit post-layout to allow for a nice
- * transition. These values determine that factor.
- */
- private float mClockCollapsedScaleFactor;
- private float mAvatarCollapsedScaleFactor;
-
- private ActivityStarter mActivityStarter;
- private BatteryController mBatteryController;
- private NextAlarmController mNextAlarmController;
- private QSPanel mQSPanel;
-
- private final Rect mClipBounds = new Rect();
-
- private boolean mCaptureValues;
- private boolean mSignalClusterDetached;
- private final LayoutValues mCollapsedValues = new LayoutValues();
- private final LayoutValues mExpandedValues = new LayoutValues();
- private final LayoutValues mCurrentValues = new LayoutValues();
-
- private float mCurrentT;
- private boolean mShowingDetail;
- private boolean mDetailTransitioning;
-
- private boolean mAllowExpand = true;
-
- public StatusBarHeaderView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mSystemIconsSuperContainer = findViewById(R.id.system_icons_super_container);
- mSystemIconsContainer = (ViewGroup) findViewById(R.id.system_icons_container);
- mSystemIconsSuperContainer.setOnClickListener(this);
- mDateGroup = findViewById(R.id.date_group);
- mClock = findViewById(R.id.clock);
- mTime = (TextView) findViewById(R.id.time_view);
- mAmPm = (TextView) findViewById(R.id.am_pm_view);
- mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
- mMultiUserAvatar = (ImageView) findViewById(R.id.multi_user_avatar);
- mDateCollapsed = (TextView) findViewById(R.id.date_collapsed);
- mDateExpanded = (TextView) findViewById(R.id.date_expanded);
- mSettingsButton = (SettingsButton) findViewById(R.id.settings_button);
- mSettingsContainer = findViewById(R.id.settings_button_container);
- mSettingsButton.setOnClickListener(this);
- mQsDetailHeader = findViewById(R.id.qs_detail_header);
- mQsDetailHeader.setAlpha(0);
- mQsDetailHeaderTitle = (TextView) mQsDetailHeader.findViewById(android.R.id.title);
- mQsDetailHeaderSwitch = (Switch) mQsDetailHeader.findViewById(android.R.id.toggle);
- mQsDetailHeaderProgress = (ImageView) findViewById(R.id.qs_detail_header_progress);
- mEmergencyCallsOnly = (TextView) findViewById(R.id.header_emergency_calls_only);
- mBatteryLevel = (TextView) findViewById(R.id.battery_level);
- mAlarmStatus = (TextView) findViewById(R.id.alarm_status);
- mAlarmStatus.setOnClickListener(this);
- mSignalCluster = findViewById(R.id.signal_cluster);
- mSystemIcons = (LinearLayout) findViewById(R.id.system_icons);
- loadDimens();
- updateVisibilities();
- updateClockScale();
- updateAvatarScale();
- addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right,
- int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
- if ((right - left) != (oldRight - oldLeft)) {
- // width changed, update clipping
- setClipping(getHeight());
- }
- boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
- mTime.setPivotX(rtl ? mTime.getWidth() : 0);
- mTime.setPivotY(mTime.getBaseline());
- updateAmPmTranslation();
- }
- });
- setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRect(mClipBounds);
- }
- });
- requestCaptureValues();
-
- // RenderThread is doing more harm than good when touching the header (to expand quick
- // settings), so disable it for this view
- ((RippleDrawable) getBackground()).setForceSoftware(true);
- ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
- ((RippleDrawable) mSystemIconsSuperContainer.getBackground()).setForceSoftware(true);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- if (mCaptureValues) {
- if (mExpanded) {
- captureLayoutValues(mExpandedValues);
- } else {
- captureLayoutValues(mCollapsedValues);
- }
- mCaptureValues = false;
- updateLayoutValues(mCurrentT);
- }
- mAlarmStatus.setX(mDateGroup.getLeft() + mDateCollapsed.getRight());
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- FontSizeUtils.updateFontSize(mBatteryLevel, R.dimen.battery_level_text_size);
- FontSizeUtils.updateFontSize(mEmergencyCallsOnly,
- R.dimen.qs_emergency_calls_only_text_size);
- FontSizeUtils.updateFontSize(mDateCollapsed, R.dimen.qs_date_collapsed_size);
- FontSizeUtils.updateFontSize(mDateExpanded, R.dimen.qs_date_collapsed_size);
- FontSizeUtils.updateFontSize(mAlarmStatus, R.dimen.qs_date_collapsed_size);
- FontSizeUtils.updateFontSize(this, android.R.id.title, R.dimen.qs_detail_header_text_size);
- FontSizeUtils.updateFontSize(this, android.R.id.toggle, R.dimen.qs_detail_header_text_size);
- FontSizeUtils.updateFontSize(mAmPm, R.dimen.qs_time_collapsed_size);
- FontSizeUtils.updateFontSize(this, R.id.empty_time_view, R.dimen.qs_time_expanded_size);
-
- mEmergencyCallsOnly.setText(com.android.internal.R.string.emergency_calls_only);
-
- mClockCollapsedSize = getResources().getDimensionPixelSize(R.dimen.qs_time_collapsed_size);
- mClockExpandedSize = getResources().getDimensionPixelSize(R.dimen.qs_time_expanded_size);
- mClockCollapsedScaleFactor = (float) mClockCollapsedSize / (float) mClockExpandedSize;
-
- updateClockScale();
- updateClockCollapsedMargin();
- }
-
- private void updateClockCollapsedMargin() {
- Resources res = getResources();
- int padding = res.getDimensionPixelSize(R.dimen.clock_collapsed_bottom_margin);
- int largePadding = res.getDimensionPixelSize(
- R.dimen.clock_collapsed_bottom_margin_large_text);
- float largeFactor = (MathUtils.constrain(getResources().getConfiguration().fontScale, 1.0f,
- FontSizeUtils.LARGE_TEXT_SCALE) - 1f) / (FontSizeUtils.LARGE_TEXT_SCALE - 1f);
- mClockMarginBottomCollapsed = Math.round((1 - largeFactor) * padding + largeFactor * largePadding);
- requestLayout();
- }
-
- private void requestCaptureValues() {
- mCaptureValues = true;
- requestLayout();
- }
-
- private void loadDimens() {
- mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.status_bar_header_height);
- mExpandedHeight = getResources().getDimensionPixelSize(
- R.dimen.status_bar_header_height_expanded);
- mMultiUserExpandedMargin =
- getResources().getDimensionPixelSize(R.dimen.multi_user_switch_expanded_margin);
- mMultiUserCollapsedMargin =
- getResources().getDimensionPixelSize(R.dimen.multi_user_switch_collapsed_margin);
- mClockMarginBottomExpanded =
- getResources().getDimensionPixelSize(R.dimen.clock_expanded_bottom_margin);
- updateClockCollapsedMargin();
- mMultiUserSwitchWidthCollapsed =
- getResources().getDimensionPixelSize(R.dimen.multi_user_switch_width_collapsed);
- mMultiUserSwitchWidthExpanded =
- getResources().getDimensionPixelSize(R.dimen.multi_user_switch_width_expanded);
- mAvatarCollapsedScaleFactor =
- getResources().getDimensionPixelSize(R.dimen.multi_user_avatar_collapsed_size)
- / (float) mMultiUserAvatar.getLayoutParams().width;
- mClockCollapsedSize = getResources().getDimensionPixelSize(R.dimen.qs_time_collapsed_size);
- mClockExpandedSize = getResources().getDimensionPixelSize(R.dimen.qs_time_expanded_size);
- mClockCollapsedScaleFactor = (float) mClockCollapsedSize / (float) mClockExpandedSize;
-
- }
-
- public void setActivityStarter(ActivityStarter activityStarter) {
- mActivityStarter = activityStarter;
- }
-
- public void setBatteryController(BatteryController batteryController) {
- mBatteryController = batteryController;
- ((BatteryMeterView) findViewById(R.id.battery)).setBatteryController(batteryController);
- }
-
- public void setNextAlarmController(NextAlarmController nextAlarmController) {
- mNextAlarmController = nextAlarmController;
- }
-
- public int getCollapsedHeight() {
- return mCollapsedHeight;
- }
-
- public int getExpandedHeight() {
- return mAllowExpand ? mExpandedHeight : mCollapsedHeight;
- }
-
- public void setListening(boolean listening) {
- if (listening == mListening) {
- return;
- }
- mListening = listening;
- updateListeners();
- }
-
- public void setExpanded(boolean expanded) {
- if (!mAllowExpand) {
- expanded = false;
- }
- boolean changed = expanded != mExpanded;
- mExpanded = expanded;
- if (changed) {
- updateEverything();
- }
- }
-
- public void updateEverything() {
- updateHeights();
- updateVisibilities();
- updateSystemIconsLayoutParams();
- updateClickTargets();
- updateMultiUserSwitch();
- updateClockScale();
- updateAvatarScale();
- updateClockLp();
- requestCaptureValues();
- }
-
- private void updateHeights() {
- int height = mExpanded ? mExpandedHeight : mCollapsedHeight;
- ViewGroup.LayoutParams lp = getLayoutParams();
- if (lp.height != height) {
- lp.height = height;
- setLayoutParams(lp);
- }
- }
-
- private void updateVisibilities() {
- mDateCollapsed.setVisibility(mExpanded && mAlarmShowing ? View.VISIBLE : View.INVISIBLE);
- mDateExpanded.setVisibility(mExpanded && mAlarmShowing ? View.INVISIBLE : View.VISIBLE);
- mAlarmStatus.setVisibility(mExpanded && mAlarmShowing ? View.VISIBLE : View.INVISIBLE);
- mSettingsContainer.setVisibility(mExpanded ? View.VISIBLE : View.INVISIBLE);
- mQsDetailHeader.setVisibility(mExpanded && mShowingDetail? View.VISIBLE : View.INVISIBLE);
- if (mSignalCluster != null) {
- updateSignalClusterDetachment();
- }
- mEmergencyCallsOnly.setVisibility(mExpanded && mShowEmergencyCallsOnly ? VISIBLE : GONE);
- mBatteryLevel.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
- mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
- TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
- }
-
- private void updateSignalClusterDetachment() {
- boolean detached = mExpanded;
- if (detached != mSignalClusterDetached) {
- if (detached) {
- getOverlay().add(mSignalCluster);
- } else {
- reattachSignalCluster();
- }
- }
- mSignalClusterDetached = detached;
- }
-
- private void reattachSignalCluster() {
- getOverlay().remove(mSignalCluster);
- mSystemIcons.addView(mSignalCluster, 1);
- }
-
- private void updateSystemIconsLayoutParams() {
- RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsSuperContainer.getLayoutParams();
- int rule = mExpanded
- ? mSettingsContainer.getId()
- : mMultiUserSwitch.getId();
- if (rule != lp.getRules()[RelativeLayout.START_OF]) {
- lp.addRule(RelativeLayout.START_OF, rule);
- mSystemIconsSuperContainer.setLayoutParams(lp);
- }
- }
-
- private void updateListeners() {
- if (mListening) {
- mBatteryController.addStateChangedCallback(this);
- mNextAlarmController.addStateChangedCallback(this);
- } else {
- mBatteryController.removeStateChangedCallback(this);
- mNextAlarmController.removeStateChangedCallback(this);
- }
- }
-
- private void updateAvatarScale() {
- if (mExpanded) {
- mMultiUserAvatar.setScaleX(1f);
- mMultiUserAvatar.setScaleY(1f);
- } else {
- mMultiUserAvatar.setScaleX(mAvatarCollapsedScaleFactor);
- mMultiUserAvatar.setScaleY(mAvatarCollapsedScaleFactor);
- }
- }
-
- private void updateClockScale() {
- mTime.setTextSize(TypedValue.COMPLEX_UNIT_PX, mExpanded
- ? mClockExpandedSize
- : mClockCollapsedSize);
- mTime.setScaleX(1f);
- mTime.setScaleY(1f);
- updateAmPmTranslation();
- }
-
- private void updateAmPmTranslation() {
- boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
- mAmPm.setTranslationX((rtl ? 1 : -1) * mTime.getWidth() * (1 - mTime.getScaleX()));
- }
-
- @Override
- public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
- String percentage = NumberFormat.getPercentInstance().format((double) level / 100.0);
- mBatteryLevel.setText(percentage);
- }
-
- @Override
- public void onPowerSaveChanged(boolean isPowerSave) {
- // could not care less
- }
-
- @Override
- public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
- mNextAlarm = nextAlarm;
- if (nextAlarm != null) {
- mAlarmStatus.setText(KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm));
- }
- mAlarmShowing = nextAlarm != null;
- updateEverything();
- requestCaptureValues();
- }
-
- private void updateClickTargets() {
- mMultiUserSwitch.setClickable(mExpanded);
- mMultiUserSwitch.setFocusable(mExpanded);
- mSystemIconsSuperContainer.setClickable(mExpanded);
- mSystemIconsSuperContainer.setFocusable(mExpanded);
- mAlarmStatus.setClickable(mNextAlarm != null && mNextAlarm.getShowIntent() != null);
- }
-
- private void updateClockLp() {
- int marginBottom = mExpanded
- ? mClockMarginBottomExpanded
- : mClockMarginBottomCollapsed;
- LayoutParams lp = (LayoutParams) mDateGroup.getLayoutParams();
- if (marginBottom != lp.bottomMargin) {
- lp.bottomMargin = marginBottom;
- mDateGroup.setLayoutParams(lp);
- }
- }
-
- private void updateMultiUserSwitch() {
- int marginEnd;
- int width;
- if (mExpanded) {
- marginEnd = mMultiUserExpandedMargin;
- width = mMultiUserSwitchWidthExpanded;
- } else {
- marginEnd = mMultiUserCollapsedMargin;
- width = mMultiUserSwitchWidthCollapsed;
- }
- MarginLayoutParams lp = (MarginLayoutParams) mMultiUserSwitch.getLayoutParams();
- if (marginEnd != lp.getMarginEnd() || lp.width != width) {
- lp.setMarginEnd(marginEnd);
- lp.width = width;
- mMultiUserSwitch.setLayoutParams(lp);
- }
- }
-
- public void setExpansion(float t) {
- if (!mExpanded) {
- t = 0f;
- }
- mCurrentT = t;
- float height = mCollapsedHeight + t * (mExpandedHeight - mCollapsedHeight);
- if (height < mCollapsedHeight) {
- height = mCollapsedHeight;
- }
- if (height > mExpandedHeight) {
- height = mExpandedHeight;
- }
- setClipping(height);
- updateLayoutValues(t);
- }
-
- private void updateLayoutValues(float t) {
- if (mCaptureValues) {
- return;
- }
- mCurrentValues.interpoloate(mCollapsedValues, mExpandedValues, t);
- applyLayoutValues(mCurrentValues);
- }
-
- private void setClipping(float height) {
- mClipBounds.set(getPaddingLeft(), 0, getWidth() - getPaddingRight(), (int) height);
- setClipBounds(mClipBounds);
- invalidateOutline();
- }
-
- public void setUserInfoController(UserInfoController userInfoController) {
- userInfoController.addListener(new UserInfoController.OnUserInfoChangedListener() {
- @Override
- public void onUserInfoChanged(String name, Drawable picture) {
- mMultiUserAvatar.setImageDrawable(picture);
- }
- });
- }
-
- @Override
- public void setCallback(Callback qsPanelCallback) {
- }
-
- @Override
- public void onClick(View v) {
- if (v == mSettingsButton) {
- if (mSettingsButton.isTunerClick()) {
- if (TunerService.isTunerEnabled(mContext)) {
- TunerService.showResetRequest(mContext, new Runnable() {
- @Override
- public void run() {
- // Relaunch settings so that the tuner disappears.
- startSettingsActivity();
- }
- });
- } else {
- Toast.makeText(getContext(), R.string.tuner_toast, Toast.LENGTH_LONG).show();
- TunerService.setTunerEnabled(mContext, true);
- }
- }
- startSettingsActivity();
- } else if (v == mSystemIconsSuperContainer) {
- startBatteryActivity();
- } else if (v == mAlarmStatus && mNextAlarm != null) {
- PendingIntent showIntent = mNextAlarm.getShowIntent();
- if (showIntent != null) {
- mActivityStarter.startPendingIntentDismissingKeyguard(showIntent);
- }
- }
- }
-
- private void startSettingsActivity() {
- mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
- true /* dismissShade */);
- }
-
- private void startBatteryActivity() {
- mActivityStarter.startActivity(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY),
- true /* dismissShade */);
- }
-
- public void setQSPanel(QSPanel qsp) {
- mQSPanel = qsp;
- if (mQSPanel != null) {
- mQSPanel.setCallback(mQsPanelCallback);
- }
- mMultiUserSwitch.setQsPanel(qsp);
- }
-
- @Override
- public boolean shouldDelayChildPressedState() {
- return true;
- }
-
- @Override
- public void setEmergencyCallsOnly(boolean show) {
- boolean changed = show != mShowEmergencyCallsOnly;
- if (changed) {
- mShowEmergencyCallsOnly = show;
- if (mExpanded) {
- updateEverything();
- requestCaptureValues();
- }
- }
- }
-
- @Override
- protected void dispatchSetPressed(boolean pressed) {
- // We don't want that everything lights up when we click on the header, so block the request
- // here.
- }
-
- private void captureLayoutValues(LayoutValues target) {
- target.timeScale = mExpanded ? 1f : mClockCollapsedScaleFactor;
- target.clockY = mClock.getBottom();
- target.dateY = mDateGroup.getTop();
- target.emergencyCallsOnlyAlpha = getAlphaForVisibility(mEmergencyCallsOnly);
- target.alarmStatusAlpha = getAlphaForVisibility(mAlarmStatus);
- target.dateCollapsedAlpha = getAlphaForVisibility(mDateCollapsed);
- target.dateExpandedAlpha = getAlphaForVisibility(mDateExpanded);
- target.avatarScale = mMultiUserAvatar.getScaleX();
- target.avatarX = mMultiUserSwitch.getLeft() + mMultiUserAvatar.getLeft();
- target.avatarY = mMultiUserSwitch.getTop() + mMultiUserAvatar.getTop();
- if (getLayoutDirection() == LAYOUT_DIRECTION_LTR) {
- target.batteryX = mSystemIconsSuperContainer.getLeft()
- + mSystemIconsContainer.getRight();
- } else {
- target.batteryX = mSystemIconsSuperContainer.getLeft()
- + mSystemIconsContainer.getLeft();
- }
- target.batteryY = mSystemIconsSuperContainer.getTop() + mSystemIconsContainer.getTop();
- target.batteryLevelAlpha = getAlphaForVisibility(mBatteryLevel);
- target.settingsAlpha = getAlphaForVisibility(mSettingsContainer);
- target.settingsTranslation = mExpanded
- ? 0
- : mMultiUserSwitch.getLeft() - mSettingsContainer.getLeft();
- target.signalClusterAlpha = mSignalClusterDetached ? 0f : 1f;
- target.settingsRotation = !mExpanded ? 90f : 0f;
- }
-
- private float getAlphaForVisibility(View v) {
- return v == null || v.getVisibility() == View.VISIBLE ? 1f : 0f;
- }
-
- private void applyAlpha(View v, float alpha) {
- if (v == null || v.getVisibility() == View.GONE) {
- return;
- }
- if (alpha == 0f) {
- v.setVisibility(View.INVISIBLE);
- } else {
- v.setVisibility(View.VISIBLE);
- v.setAlpha(alpha);
- }
- }
-
- private void applyLayoutValues(LayoutValues values) {
- mTime.setScaleX(values.timeScale);
- mTime.setScaleY(values.timeScale);
- mClock.setY(values.clockY - mClock.getHeight());
- mDateGroup.setY(values.dateY);
- mAlarmStatus.setY(values.dateY - mAlarmStatus.getPaddingTop());
- mMultiUserAvatar.setScaleX(values.avatarScale);
- mMultiUserAvatar.setScaleY(values.avatarScale);
- mMultiUserAvatar.setX(values.avatarX - mMultiUserSwitch.getLeft());
- mMultiUserAvatar.setY(values.avatarY - mMultiUserSwitch.getTop());
- if (getLayoutDirection() == LAYOUT_DIRECTION_LTR) {
- mSystemIconsSuperContainer.setX(values.batteryX - mSystemIconsContainer.getRight());
- } else {
- mSystemIconsSuperContainer.setX(values.batteryX - mSystemIconsContainer.getLeft());
- }
- mSystemIconsSuperContainer.setY(values.batteryY - mSystemIconsContainer.getTop());
- if (mSignalCluster != null && mExpanded) {
- if (getLayoutDirection() == LAYOUT_DIRECTION_LTR) {
- mSignalCluster.setX(mSystemIconsSuperContainer.getX()
- - mSignalCluster.getWidth());
- } else {
- mSignalCluster.setX(mSystemIconsSuperContainer.getX()
- + mSystemIconsSuperContainer.getWidth());
- }
- mSignalCluster.setY(
- mSystemIconsSuperContainer.getY() + mSystemIconsSuperContainer.getHeight()/2
- - mSignalCluster.getHeight()/2);
- } else if (mSignalCluster != null) {
- mSignalCluster.setTranslationX(0f);
- mSignalCluster.setTranslationY(0f);
- }
- if (!mSettingsButton.isAnimating()) {
- mSettingsContainer.setTranslationY(mSystemIconsSuperContainer.getTranslationY());
- mSettingsContainer.setTranslationX(values.settingsTranslation);
- mSettingsButton.setRotation(values.settingsRotation);
- }
- applyAlpha(mEmergencyCallsOnly, values.emergencyCallsOnlyAlpha);
- if (!mShowingDetail && !mDetailTransitioning) {
- // Otherwise it needs to stay invisible
- applyAlpha(mAlarmStatus, values.alarmStatusAlpha);
- }
- applyAlpha(mDateCollapsed, values.dateCollapsedAlpha);
- applyAlpha(mDateExpanded, values.dateExpandedAlpha);
- applyAlpha(mBatteryLevel, values.batteryLevelAlpha);
- applyAlpha(mSettingsContainer, values.settingsAlpha);
- applyAlpha(mSignalCluster, values.signalClusterAlpha);
- if (!mExpanded) {
- mTime.setScaleX(1f);
- mTime.setScaleY(1f);
- }
- updateAmPmTranslation();
- }
-
- /**
- * Captures all layout values (position, visibility) for a certain state. This is used for
- * animations.
- */
- private static final class LayoutValues {
-
- float dateExpandedAlpha;
- float dateCollapsedAlpha;
- float emergencyCallsOnlyAlpha;
- float alarmStatusAlpha;
- float timeScale = 1f;
- float clockY;
- float dateY;
- float avatarScale;
- float avatarX;
- float avatarY;
- float batteryX;
- float batteryY;
- float batteryLevelAlpha;
- float settingsAlpha;
- float settingsTranslation;
- float signalClusterAlpha;
- float settingsRotation;
-
- public void interpoloate(LayoutValues v1, LayoutValues v2, float t) {
- timeScale = v1.timeScale * (1 - t) + v2.timeScale * t;
- clockY = v1.clockY * (1 - t) + v2.clockY * t;
- dateY = v1.dateY * (1 - t) + v2.dateY * t;
- avatarScale = v1.avatarScale * (1 - t) + v2.avatarScale * t;
- avatarX = v1.avatarX * (1 - t) + v2.avatarX * t;
- avatarY = v1.avatarY * (1 - t) + v2.avatarY * t;
- batteryX = v1.batteryX * (1 - t) + v2.batteryX * t;
- batteryY = v1.batteryY * (1 - t) + v2.batteryY * t;
- settingsTranslation = v1.settingsTranslation * (1 - t) + v2.settingsTranslation * t;
-
- float t1 = Math.max(0, t - 0.5f) * 2;
- settingsRotation = v1.settingsRotation * (1 - t1) + v2.settingsRotation * t1;
- emergencyCallsOnlyAlpha =
- v1.emergencyCallsOnlyAlpha * (1 - t1) + v2.emergencyCallsOnlyAlpha * t1;
-
- float t2 = Math.min(1, 2 * t);
- signalClusterAlpha = v1.signalClusterAlpha * (1 - t2) + v2.signalClusterAlpha * t2;
-
- float t3 = Math.max(0, t - 0.7f) / 0.3f;
- batteryLevelAlpha = v1.batteryLevelAlpha * (1 - t3) + v2.batteryLevelAlpha * t3;
- settingsAlpha = v1.settingsAlpha * (1 - t3) + v2.settingsAlpha * t3;
- dateExpandedAlpha = v1.dateExpandedAlpha * (1 - t3) + v2.dateExpandedAlpha * t3;
- dateCollapsedAlpha = v1.dateCollapsedAlpha * (1 - t3) + v2.dateCollapsedAlpha * t3;
- alarmStatusAlpha = v1.alarmStatusAlpha * (1 - t3) + v2.alarmStatusAlpha * t3;
- }
- }
-
- private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
- private boolean mScanState;
-
- @Override
- public void onToggleStateChanged(final boolean state) {
- post(new Runnable() {
- @Override
- public void run() {
- handleToggleStateChanged(state);
- }
- });
- }
-
- @Override
- public void onShowingDetail(final DetailAdapter detail, int x, int y) {
- mDetailTransitioning = true;
- post(new Runnable() {
- @Override
- public void run() {
- handleShowingDetail(detail);
- }
- });
- }
-
- @Override
- public void onScanStateChanged(final boolean state) {
- post(new Runnable() {
- @Override
- public void run() {
- handleScanStateChanged(state);
- }
- });
- }
-
- private void handleToggleStateChanged(boolean state) {
- mQsDetailHeaderSwitch.setChecked(state);
- }
-
- private void handleScanStateChanged(boolean state) {
- if (mScanState == state) return;
- mScanState = state;
- final Animatable anim = (Animatable) mQsDetailHeaderProgress.getDrawable();
- if (state) {
- mQsDetailHeaderProgress.animate().alpha(1f);
- anim.start();
- } else {
- mQsDetailHeaderProgress.animate().alpha(0f);
- anim.stop();
- }
- }
-
- private void handleShowingDetail(final QSTile.DetailAdapter detail) {
- final boolean showingDetail = detail != null;
- transition(mClock, !showingDetail);
- transition(mDateGroup, !showingDetail);
- if (mAlarmShowing) {
- transition(mAlarmStatus, !showingDetail);
- }
- transition(mQsDetailHeader, showingDetail);
- mShowingDetail = showingDetail;
- if (showingDetail) {
- mQsDetailHeaderTitle.setText(detail.getTitle());
- final Boolean toggleState = detail.getToggleState();
- if (toggleState == null) {
- mQsDetailHeaderSwitch.setVisibility(INVISIBLE);
- mQsDetailHeader.setClickable(false);
- } else {
- mQsDetailHeaderSwitch.setVisibility(VISIBLE);
- mQsDetailHeaderSwitch.setChecked(toggleState);
- mQsDetailHeader.setClickable(true);
- mQsDetailHeader.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- boolean checked = !mQsDetailHeaderSwitch.isChecked();
- mQsDetailHeaderSwitch.setChecked(checked);
- detail.setToggleState(checked);
- }
- });
- }
- } else {
- mQsDetailHeader.setClickable(false);
- }
- }
-
- private void transition(final View v, final boolean in) {
- if (in) {
- v.bringToFront();
- v.setVisibility(VISIBLE);
- }
- if (v.hasOverlappingRendering()) {
- v.animate().withLayer();
- }
- v.animate()
- .alpha(in ? 1 : 0)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- if (!in) {
- v.setVisibility(INVISIBLE);
- }
- mDetailTransitioning = false;
- }
- })
- .start();
- }
- };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 716a44e..3326736 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -334,11 +334,12 @@
public void dump(PrintWriter pw) {
int N = mStatusIcons.getChildCount();
- pw.println(" system icons: " + N);
+ pw.println(" icon views: " + N);
for (int i=0; i<N; i++) {
StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i);
pw.println(" [" + i + "] icon=" + ic);
}
+ super.dump(pw);
}
public void dispatchDemoCommand(String command, Bundle args) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
index 94adea0..660672d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
@@ -78,9 +78,9 @@
public void dump(PrintWriter pw) {
final int N = mSlots.size();
- pw.println("Icon list:");
+ pw.println(" icon slots: " + N);
for (int i=0; i<N; i++) {
- pw.printf(" %2d: (%s) %s\n", i, mSlots.get(i), mIcons.get(i));
+ pw.printf(" %2d: (%s) %s\n", i, mSlots.get(i), mIcons.get(i));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 8d0d9cb..c72f994 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -53,6 +53,11 @@
private static final long WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS = 200;
+ // Duration of the Keyguard dismissal animation in case the user is currently locked. This is to
+ // make everything a bit slower to bridge a gap until the user is unlocked and home screen has
+ // dranw its first frame.
+ private static final long KEYGUARD_DISMISS_DURATION_LOCKED = 2000;
+
private static String TAG = "StatusBarKeyguardViewManager";
protected final Context mContext;
@@ -180,13 +185,17 @@
}
public void onStartedWakingUp() {
+ Trace.beginSection("StatusBarKeyguardViewManager#onStartedWakingUp");
mDeviceInteractive = true;
mDeviceWillWakeUp = false;
mPhoneStatusBar.onStartedWakingUp();
+ Trace.endSection();
}
public void onScreenTurningOn() {
+ Trace.beginSection("StatusBarKeyguardViewManager#onScreenTurningOn");
mPhoneStatusBar.onScreenTurningOn();
+ Trace.endSection();
}
public boolean isScreenTurnedOn() {
@@ -194,6 +203,7 @@
}
public void onScreenTurnedOn() {
+ Trace.beginSection("StatusBarKeyguardViewManager#onScreenTurnedOn");
mScreenTurnedOn = true;
if (mDeferScrimFadeOut) {
mDeferScrimFadeOut = false;
@@ -202,6 +212,7 @@
updateStates();
}
mPhoneStatusBar.onScreenTurnedOn();
+ Trace.endSection();
}
@Override
@@ -274,9 +285,12 @@
/**
* Hides the keyguard view
*/
- public void hide(long startTime, final long fadeoutDuration) {
+ public void hide(long startTime, long fadeoutDuration) {
mShowing = false;
+ if (!KeyguardUpdateMonitor.getInstance(mContext).isUserUnlocked()) {
+ fadeoutDuration = KEYGUARD_DISMISS_DURATION_LOCKED;
+ }
long uptimeMillis = SystemClock.uptimeMillis();
long delay = Math.max(0, startTime + HIDE_TIMING_CORRECTION_MS - uptimeMillis);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index 888e19c..a7a792b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -16,11 +16,16 @@
package com.android.systemui.statusbar.phone;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.graphics.PixelFormat;
+import android.os.RemoteException;
import android.os.SystemProperties;
+import android.os.Trace;
+import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
@@ -41,11 +46,16 @@
*/
public class StatusBarWindowManager implements RemoteInputController.Callback {
+ private static final String TAG = "StatusBarWindowManager";
+
private final Context mContext;
private final WindowManager mWindowManager;
+ private final IActivityManager mActivityManager;
private View mStatusBarView;
private WindowManager.LayoutParams mLp;
private WindowManager.LayoutParams mLpChanged;
+ private boolean mHasTopUi;
+ private boolean mHasTopUiChanged;
private int mBarHeight;
private final boolean mKeyguardScreenRotation;
private final float mScreenBrightnessDoze;
@@ -54,6 +64,7 @@
public StatusBarWindowManager(Context context) {
mContext = context;
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mActivityManager = ActivityManagerNative.getDefault();
mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
mScreenBrightnessDoze = mContext.getResources().getInteger(
com.android.internal.R.integer.config_screenBrightnessDoze) / 255f;
@@ -157,7 +168,11 @@
}
private void applyFitsSystemWindows(State state) {
- mStatusBarView.setFitsSystemWindows(!state.isKeyguardShowingAndNotOccluded());
+ boolean fitsSystemWindows = !state.isKeyguardShowingAndNotOccluded();
+ if (mStatusBarView.getFitsSystemWindows() != fitsSystemWindows) {
+ mStatusBarView.setFitsSystemWindows(fitsSystemWindows);
+ mStatusBarView.requestApplyInsets();
+ }
}
private void applyUserActivityTimeout(State state) {
@@ -193,9 +208,18 @@
applyFitsSystemWindows(state);
applyModalFlag(state);
applyBrightness(state);
+ applyHasTopUi(state);
if (mLp.copyFrom(mLpChanged) != 0) {
mWindowManager.updateViewLayout(mStatusBarView, mLp);
}
+ if (mHasTopUi != mHasTopUiChanged) {
+ try {
+ mActivityManager.setHasTopUi(mHasTopUiChanged);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to call setHasTopUi", e);
+ }
+ mHasTopUi = mHasTopUiChanged;
+ }
}
private void applyForceStatusBarVisibleFlag(State state) {
@@ -224,6 +248,10 @@
}
}
+ private void applyHasTopUi(State state) {
+ mHasTopUiChanged = isExpanded(state);
+ }
+
public void setKeyguardShowing(boolean showing) {
mCurrentState.keyguardShowing = showing;
apply(mCurrentState);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index ebfa018..47ea59e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -70,6 +70,7 @@
private View mBrightnessMirror;
private int mRightInset = 0;
+ private int mLeftInset = 0;
private PhoneStatusBar mService;
private final Paint mTransparentSrcPaint = new Paint();
@@ -93,25 +94,26 @@
@Override
protected boolean fitSystemWindows(Rect insets) {
if (getFitsSystemWindows()) {
- boolean paddingChanged = insets.left != getPaddingLeft()
- || insets.top != getPaddingTop()
+ boolean paddingChanged = insets.top != getPaddingTop()
|| insets.bottom != getPaddingBottom();
// Super-special right inset handling, because scrims and backdrop need to ignore it.
- if (insets.right != mRightInset) {
+ if (insets.right != mRightInset || insets.left != mLeftInset) {
mRightInset = insets.right;
+ mLeftInset = insets.left;
applyMargins();
}
- // Drop top inset, apply left inset and pass through bottom inset.
+ // Drop top inset, and pass through bottom inset.
if (paddingChanged) {
- setPadding(insets.left, 0, 0, 0);
+ setPadding(0, 0, 0, 0);
}
insets.left = 0;
insets.top = 0;
insets.right = 0;
} else {
- if (mRightInset != 0) {
+ if (mRightInset != 0 || mLeftInset != 0) {
mRightInset = 0;
+ mLeftInset = 0;
applyMargins();
}
boolean changed = getPaddingLeft() != 0
@@ -127,13 +129,16 @@
}
private void applyMargins() {
+ mService.mScrimController.setLeftInset(mLeftInset);
final int N = getChildCount();
for (int i = 0; i < N; i++) {
View child = getChildAt(i);
if (child.getLayoutParams() instanceof LayoutParams) {
LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (!lp.ignoreRightInset && lp.rightMargin != mRightInset) {
+ if (!lp.ignoreRightInset
+ && (lp.rightMargin != mRightInset || lp.leftMargin != mLeftInset)) {
lp.rightMargin = mRightInset;
+ lp.leftMargin = mLeftInset;
child.requestLayout();
}
}
@@ -185,6 +190,12 @@
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
+ if (mService.interceptMediaKey(event)) {
+ return true;
+ }
+ if (super.dispatchKeyEvent(event)) {
+ return true;
+ }
boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_BACK:
@@ -209,10 +220,7 @@
}
break;
}
- if (mService.interceptMediaKey(event)) {
- return true;
- }
- return super.dispatchKeyEvent(event);
+ return false;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
index a91cd51..7e92edf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import android.content.Context;
+import android.os.Trace;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -85,6 +86,7 @@
}
private void update(boolean updateAlways) {
+ Trace.beginSection("UnlockMethodCache#update");
int user = KeyguardUpdateMonitor.getCurrentUser();
boolean secure = mLockPatternUtils.isSecure(user);
boolean canSkipBouncer = !secure || mKeyguardUpdateMonitor.getUserCanSkipBouncer(user);
@@ -102,6 +104,7 @@
mFaceUnlockRunning = faceUnlockRunning;
notifyListeners();
}
+ Trace.endSection();
}
private void notifyListeners() {
@@ -133,10 +136,13 @@
@Override
public void onFingerprintAuthenticated(int userId) {
+ Trace.beginSection("KeyguardUpdateMonitorCallback#onFingerprintAuthenticated");
if (!mKeyguardUpdateMonitor.isUnlockingWithFingerprintAllowed()) {
+ Trace.endSection();
return;
}
update(false /* updateAlways */);
+ Trace.endSection();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/touch_analytics.proto b/packages/SystemUI/src/com/android/systemui/statusbar/phone/touch_analytics.proto
index afc8f77..50fd52a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/touch_analytics.proto
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/touch_analytics.proto
@@ -120,6 +120,7 @@
RESERVED_2 = 1;
RANDOM_WAKEUP = 2;
REAL = 3;
+ REJECTED_TOUCH_REPORT = 4;
}
optional uint64 startTimestampMillis = 1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 5d734c6..b9c7a4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -89,14 +89,18 @@
@Override
public void addStateChangedCallback(BatteryController.BatteryStateChangeCallback cb) {
- mChangeCallbacks.add(cb);
+ synchronized (mChangeCallbacks) {
+ mChangeCallbacks.add(cb);
+ }
cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
cb.onPowerSaveChanged(mPowerSave);
}
@Override
public void removeStateChangedCallback(BatteryController.BatteryStateChangeCallback cb) {
- mChangeCallbacks.remove(cb);
+ synchronized (mChangeCallbacks) {
+ mChangeCallbacks.remove(cb);
+ }
}
@Override
@@ -171,16 +175,20 @@
}
protected void fireBatteryLevelChanged() {
- final int N = mChangeCallbacks.size();
- for (int i = 0; i < N; i++) {
- mChangeCallbacks.get(i).onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
+ synchronized (mChangeCallbacks) {
+ final int N = mChangeCallbacks.size();
+ for (int i = 0; i < N; i++) {
+ mChangeCallbacks.get(i).onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
+ }
}
}
private void firePowerSaveChanged() {
- final int N = mChangeCallbacks.size();
- for (int i = 0; i < N; i++) {
- mChangeCallbacks.get(i).onPowerSaveChanged(mPowerSave);
+ synchronized (mChangeCallbacks) {
+ final int N = mChangeCallbacks.size();
+ for (int i = 0; i < N; i++) {
+ mChangeCallbacks.get(i).onPowerSaveChanged(mPowerSave);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
index e7e2ac2..08675c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
@@ -26,6 +26,9 @@
boolean isBluetoothSupported();
boolean isBluetoothEnabled();
+
+ int getBluetoothState();
+
boolean isBluetoothConnected();
boolean isBluetoothConnecting();
String getLastDeviceName();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 014cc49..4f880b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -49,6 +49,7 @@
private CachedBluetoothDevice mLastDevice;
private final H mHandler = new H();
+ private int mState;
public BluetoothControllerImpl(Context context, Looper bgLooper) {
mLocalBluetoothManager = LocalBluetoothManager.getInstance(context, null);
@@ -120,6 +121,11 @@
}
@Override
+ public int getBluetoothState() {
+ return mState;
+ }
+
+ @Override
public boolean isBluetoothConnected() {
return mConnectionState == BluetoothAdapter.STATE_CONNECTED;
}
@@ -192,7 +198,9 @@
@Override
public void onBluetoothStateChanged(int bluetoothState) {
- mEnabled = bluetoothState == BluetoothAdapter.STATE_ON;
+ mEnabled = bluetoothState == BluetoothAdapter.STATE_ON
+ || bluetoothState == BluetoothAdapter.STATE_TURNING_ON;
+ mState = bluetoothState;
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
index 9a21a1e..91b21ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.policy;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
@@ -42,6 +43,7 @@
private static final int DISPATCH_AVAILABILITY_CHANGED = 2;
private final CameraManager mCameraManager;
+ private final Context mContext;
/** Call {@link #ensureHandler()} before using */
private Handler mHandler;
@@ -51,20 +53,22 @@
/** Lock on {@code this} when accessing */
private boolean mFlashlightEnabled;
- private final String mCameraId;
+ private String mCameraId;
private boolean mTorchAvailable;
- public FlashlightController(Context mContext) {
+ public FlashlightController(Context context) {
+ mContext = context;
mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
- String cameraId = null;
+ tryInitCamera();
+ }
+
+ private void tryInitCamera() {
try {
- cameraId = getCameraId();
+ mCameraId = getCameraId();
} catch (Throwable e) {
Log.e(TAG, "Couldn't initialize.", e);
return;
- } finally {
- mCameraId = cameraId;
}
if (mCameraId != null) {
@@ -94,7 +98,7 @@
}
public boolean hasFlashlight() {
- return mCameraId != null;
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
}
public synchronized boolean isEnabled() {
@@ -107,6 +111,9 @@
public void addListener(FlashlightListener l) {
synchronized (mListeners) {
+ if (mCameraId == null) {
+ tryInitCamera();
+ }
cleanUpListenersLocked(l);
mListeners.add(new WeakReference<>(l));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index c8c824a..61bac2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
+import android.annotation.DrawableRes;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Configuration;
@@ -42,11 +44,12 @@
import android.widget.ImageView;
import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.ButtonDispatcher;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
-public class KeyButtonView extends ImageView {
+public class KeyButtonView extends ImageView implements ButtonDispatcher.ButtonInterface {
private int mContentDescriptionRes;
private long mDownTime;
@@ -247,10 +250,31 @@
InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
+ @Override
public void abortCurrentGesture() {
setPressed(false);
mGestureAborted = true;
}
+
+ @Override
+ public void setImageResource(@DrawableRes int resId) {
+ super.setImageResource(resId);
+ }
+
+ @Override
+ public void setImageDrawable(@Nullable Drawable drawable) {
+ super.setImageDrawable(drawable);
+ }
+
+ @Override
+ public void setLandscape(boolean landscape) {
+ //no op
+ }
+
+ @Override
+ public void setCarMode(boolean carMode) {
+ // no op
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
index 970fed0..c175180 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
@@ -112,6 +112,10 @@
notifyKeyguardChanged();
}
+ public boolean isDeviceInteractive() {
+ return mKeyguardUpdateMonitor.isDeviceInteractive();
+ }
+
private void updateCanSkipBouncerState() {
mCanSkipBouncer = mKeyguardUpdateMonitor.getUserCanSkipBouncer(mCurrentUser);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 83e25eb..83463e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -197,8 +197,22 @@
if (mConfig.show4gForLte) {
mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.FOUR_G);
+ if (mConfig.hideLtePlus) {
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
+ TelephonyIcons.FOUR_G);
+ } else {
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
+ TelephonyIcons.FOUR_G_PLUS);
+ }
} else {
mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.LTE);
+ if (mConfig.hideLtePlus) {
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
+ TelephonyIcons.LTE);
+ } else {
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
+ TelephonyIcons.LTE_PLUS);
+ }
}
mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_IWLAN, TelephonyIcons.WFC);
}
@@ -457,6 +471,10 @@
}
mServiceState = state;
mDataNetType = state.getDataNetworkType();
+ if (mDataNetType == TelephonyManager.NETWORK_TYPE_LTE && mServiceState != null &&
+ mServiceState.isUsingCarrierAggregation()) {
+ mDataNetType = TelephonyManager.NETWORK_TYPE_LTE_CA;
+ }
updateTelephony();
}
@@ -468,6 +486,10 @@
}
mDataState = state;
mDataNetType = networkType;
+ if (mDataNetType == TelephonyManager.NETWORK_TYPE_LTE && mServiceState != null &&
+ mServiceState.isUsingCarrierAggregation()) {
+ mDataNetType = TelephonyManager.NETWORK_TYPE_LTE_CA;
+ }
updateTelephony();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index a633241..7893a1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -781,10 +781,12 @@
datatype.equals("1x") ? TelephonyIcons.ONE_X :
datatype.equals("3g") ? TelephonyIcons.THREE_G :
datatype.equals("4g") ? TelephonyIcons.FOUR_G :
+ datatype.equals("4g+") ? TelephonyIcons.FOUR_G_PLUS :
datatype.equals("e") ? TelephonyIcons.E :
datatype.equals("g") ? TelephonyIcons.G :
datatype.equals("h") ? TelephonyIcons.H :
datatype.equals("lte") ? TelephonyIcons.LTE :
+ datatype.equals("lte+") ? TelephonyIcons.LTE_PLUS :
datatype.equals("roam") ? TelephonyIcons.ROAMING :
TelephonyIcons.UNKNOWN;
}
@@ -810,7 +812,7 @@
private SubscriptionInfo addSignalController(int id, int simSlotIndex) {
SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0,
- null, 0, 0, "", SubscriptionManager.SIM_PROVISIONED);
+ null, 0, 0, "");
mMobileSignalControllers.put(id, new MobileSignalController(mContext,
mConfig, mHasMobileDataFeature, mPhone, mCallbackHandler, this, info,
mSubDefaults, mReceiverHandler.getLooper()));
@@ -850,6 +852,7 @@
boolean showAtLeast3G = false;
boolean alwaysShowCdmaRssi = false;
boolean show4gForLte = false;
+ boolean hideLtePlus = false;
boolean hspaDataDistinguishable;
static Config readConfig(Context context) {
@@ -862,6 +865,7 @@
config.show4gForLte = res.getBoolean(R.bool.config_show4GForLTE);
config.hspaDataDistinguishable =
res.getBoolean(R.bool.config_hspa_data_distinguishable);
+ config.hideLtePlus = res.getBoolean(R.bool.config_hideLtePlus);
return config;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NightModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NightModeController.java
deleted file mode 100644
index 4611ef9..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NightModeController.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.opengl.Matrix;
-import android.provider.Settings.Secure;
-import android.util.MathUtils;
-import com.android.systemui.tuner.TunerService;
-
-import java.util.ArrayList;
-
-/**
- * Listens for changes to twilight from the TwilightService.
- *
- * Also pushes the current matrix to accessibility based on the current twilight
- * and various tuner settings.
- */
-public class NightModeController implements TunerService.Tunable {
-
- public static final String NIGHT_MODE_ADJUST_TINT = "tuner_night_mode_adjust_tint";
- private static final String COLOR_MATRIX_CUSTOM_VALUES = "tuner_color_custom_values";
-
- private static final String ACTION_TWILIGHT_CHANGED = "android.intent.action.TWILIGHT_CHANGED";
-
- private static final String EXTRA_IS_NIGHT = "isNight";
- private static final String EXTRA_AMOUNT = "amount";
-
- // Night mode ~= 3400 K
- private static final float[] NIGHT_VALUES = new float[] {
- 1, 0, 0, 0,
- 0, .754f, 0, 0,
- 0, 0, .516f, 0,
- 0, 0, 0, 1,
- };
- public static final float[] IDENTITY_MATRIX = new float[] {
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 0, 0, 0, 1,
- };
-
- private final ArrayList<Listener> mListeners = new ArrayList<>();
-
- private final Context mContext;
-
- // This is whether or not this is the main NightMode controller in SysUI that should be
- // updating relevant color matrixes or if its in the tuner process getting current state
- // for UI.
- private final boolean mUpdateMatrix;
-
- private float[] mCustomMatrix;
- private boolean mListening;
- private boolean mAdjustTint;
-
- private boolean mIsNight;
- private float mAmount;
- private boolean mIsAuto;
-
- public NightModeController(Context context) {
- this(context, false);
- }
-
- public NightModeController(Context context, boolean updateMatrix) {
- mContext = context;
- mUpdateMatrix = updateMatrix;
- TunerService.get(mContext).addTunable(this, NIGHT_MODE_ADJUST_TINT,
- COLOR_MATRIX_CUSTOM_VALUES, Secure.TWILIGHT_MODE);
- }
-
- public void setNightMode(boolean isNight) {
- if (mIsAuto) {
- if (mIsNight != isNight) {
- TunerService.get(mContext).setValue(Secure.TWILIGHT_MODE, isNight
- ? Secure.TWILIGHT_MODE_AUTO_OVERRIDE_ON
- : Secure.TWILIGHT_MODE_AUTO_OVERRIDE_OFF);
- } else {
- TunerService.get(mContext).setValue(Secure.TWILIGHT_MODE,
- Secure.TWILIGHT_MODE_AUTO);
- }
- } else {
- TunerService.get(mContext).setValue(Secure.TWILIGHT_MODE, isNight
- ? Secure.TWILIGHT_MODE_LOCKED_ON : Secure.TWILIGHT_MODE_LOCKED_OFF);
- }
- }
-
- public void setAuto(boolean auto) {
- mIsAuto = auto;
- if (auto) {
- TunerService.get(mContext).setValue(Secure.TWILIGHT_MODE, Secure.TWILIGHT_MODE_AUTO);
- } else {
- // Lock into the current state
- TunerService.get(mContext).setValue(Secure.TWILIGHT_MODE, mIsNight
- ? Secure.TWILIGHT_MODE_LOCKED_ON : Secure.TWILIGHT_MODE_LOCKED_OFF);
- }
- }
-
- public boolean isAuto() {
- return mIsAuto;
- }
-
- public void setAdjustTint(Boolean newValue) {
- TunerService.get(mContext).setValue(NIGHT_MODE_ADJUST_TINT, ((Boolean) newValue) ? 1 : 0);
- }
-
- public void addListener(Listener listener) {
- mListeners.add(listener);
- listener.onNightModeChanged();
- updateListening();
- }
-
- public void removeListener(Listener listener) {
- mListeners.remove(listener);
- updateListening();
- }
-
- private void updateListening() {
- boolean shouldListen = mListeners.size() != 0 || (mUpdateMatrix && mAdjustTint);
- if (shouldListen == mListening) return;
- mListening = shouldListen;
- if (mListening) {
- mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_TWILIGHT_CHANGED));
- } else {
- mContext.unregisterReceiver(mReceiver);
- }
- }
-
- public boolean isEnabled() {
- if (!mListening) {
- updateNightMode(mContext.registerReceiver(null,
- new IntentFilter(ACTION_TWILIGHT_CHANGED)));
- }
- return mIsNight;
- }
-
- public String getCustomValues() {
- return TunerService.get(mContext).getValue(COLOR_MATRIX_CUSTOM_VALUES);
- }
-
- public void setCustomValues(String values) {
- TunerService.get(mContext).setValue(COLOR_MATRIX_CUSTOM_VALUES, values);
- }
-
- @Override
- public void onTuningChanged(String key, String newValue) {
- if (COLOR_MATRIX_CUSTOM_VALUES.equals(key)) {
- mCustomMatrix = newValue != null ? toValues(newValue) : null;
- updateCurrentMatrix();
- } else if (NIGHT_MODE_ADJUST_TINT.equals(key)) {
- mAdjustTint = newValue == null || Integer.parseInt(newValue) != 0;
- updateListening();
- updateCurrentMatrix();
- } else if (Secure.TWILIGHT_MODE.equals(key)) {
- mIsAuto = newValue != null && Integer.parseInt(newValue) >= Secure.TWILIGHT_MODE_AUTO;
- }
- }
-
- private void updateCurrentMatrix() {
- if (!mUpdateMatrix) return;
- if ((!mAdjustTint || mAmount == 0) && mCustomMatrix == null) {
- TunerService.get(mContext).setValue(Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, null);
- return;
- }
- float[] values = scaleValues(IDENTITY_MATRIX, NIGHT_VALUES, mAdjustTint ? mAmount : 0);
- if (mCustomMatrix != null) {
- values = multiply(values, mCustomMatrix);
- }
- TunerService.get(mContext).setValue(Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX,
- toString(values));
- }
-
- private void updateNightMode(Intent intent) {
- mIsNight = intent != null && intent.getBooleanExtra(EXTRA_IS_NIGHT, false);
- mAmount = intent != null ? intent.getFloatExtra(EXTRA_AMOUNT, 0) : 0;
- }
-
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (ACTION_TWILIGHT_CHANGED.equals(intent.getAction())) {
- updateNightMode(intent);
- updateCurrentMatrix();
- for (int i = 0; i < mListeners.size(); i++) {
- mListeners.get(i).onNightModeChanged();
- }
- }
- }
- };
-
- public interface Listener {
- void onNightModeChanged();
- void onTwilightAutoChanged();
- }
-
- private static float[] multiply(float[] matrix, float[] other) {
- if (matrix == null) {
- return other;
- }
- float[] result = new float[16];
- Matrix.multiplyMM(result, 0, matrix, 0, other, 0);
- return result;
- }
-
- private float[] scaleValues(float[] identityMatrix, float[] nightValues, float amount) {
- float[] values = new float[identityMatrix.length];
- for (int i = 0; i < values.length; i++) {
- values[i] = MathUtils.lerp(identityMatrix[i], nightValues[i], amount);
- }
- return values;
- }
-
- public static String toString(float[] values) {
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < values.length; i++) {
- if (builder.length() != 0) {
- builder.append(',');
- }
- builder.append(values[i]);
- }
- return builder.toString();
- }
-
- public static float[] toValues(String customValues) {
- String[] strValues = customValues.split(",");
- float[] values = new float[strValues.length];
- for (int i = 0; i < values.length; i++) {
- values[i] = Float.parseFloat(strValues[i]);
- }
- return values;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java
index 93d0ec3..687b83a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java
@@ -119,13 +119,16 @@
private WidgetInfo getWidgetInfo(Intent intent) {
PackageManager packageManager = mContext.getPackageManager();
+ int flags = PackageManager.MATCH_DEFAULT_ONLY
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
- intent, PackageManager.MATCH_DEFAULT_ONLY, KeyguardUpdateMonitor.getCurrentUser());
+ intent, flags, KeyguardUpdateMonitor.getCurrentUser());
if (appList.size() == 0) {
return null;
}
ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
- PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
+ flags | PackageManager.GET_META_DATA,
KeyguardUpdateMonitor.getCurrentUser());
if (wouldLaunchResolverActivity(resolved, appList)) {
return null;
@@ -139,23 +142,32 @@
public static boolean wouldLaunchResolverActivity(Context ctx, Intent intent,
int currentUserId) {
- return getTargetActivityInfo(ctx, intent, currentUserId) == null;
+ return getTargetActivityInfo(ctx, intent, currentUserId, false /* onlyDirectBootAware */)
+ == null;
}
/**
+ * @param onlyDirectBootAware a boolean indicating whether the matched activity packages must
+ * be direct boot aware when in direct boot mode if false, all
+ * packages are considered a match even if they are not aware.
* @return the target activity info of the intent it resolves to a specific package or
* {@code null} if it resolved to the resolver activity
*/
public static ActivityInfo getTargetActivityInfo(Context ctx, Intent intent,
- int currentUserId) {
+ int currentUserId, boolean onlyDirectBootAware) {
PackageManager packageManager = ctx.getPackageManager();
+ int flags = PackageManager.MATCH_DEFAULT_ONLY;
+ if (!onlyDirectBootAware) {
+ flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+ }
final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
- intent, PackageManager.MATCH_DEFAULT_ONLY, currentUserId);
+ intent, flags, currentUserId);
if (appList.size() == 0) {
return null;
}
ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
- PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA, currentUserId);
+ flags | PackageManager.GET_META_DATA, currentUserId);
if (resolved == null || wouldLaunchResolverActivity(resolved, appList)) {
return null;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 7704a07..cdd452f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -16,11 +16,14 @@
package com.android.systemui.statusbar.policy;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ShortcutManager;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -32,6 +35,7 @@
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.inputmethod.CompletionInfo;
@@ -46,11 +50,13 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.stack.ScrollContainer;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
/**
* Host for the remote input.
@@ -76,6 +82,10 @@
private View mScrollContainerChild;
private boolean mRemoved;
+ private int mRevealCx;
+ private int mRevealCy;
+ private int mRevealR;
+
public RemoteInputView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -132,6 +142,15 @@
mEditText.mShowImeOnInputConnection = false;
mController.remoteInputSent(mEntry);
+ // Tell ShortcutManager that this package has been "activated". ShortcutManager
+ // will reset the throttling for this package.
+ // Strictly speaking, the intent receiver may be different from the notification publisher,
+ // but that's an edge case, and also because we can't always know which package will receive
+ // an intent, so we just reset for the publisher.
+ getContext().getSystemService(ShortcutManager.class).onApplicationActive(
+ mEntry.notification.getPackageName(),
+ mEntry.notification.getUser().getIdentifier());
+
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_SEND,
mEntry.notification.getPackageName());
try {
@@ -170,14 +189,28 @@
return true;
}
- public void onDefocus() {
+ private void onDefocus(boolean animate) {
mController.removeRemoteInput(mEntry);
mEntry.remoteInputText = mEditText.getText();
// During removal, we get reattached and lose focus. Not hiding in that
// case to prevent flicker.
if (!mRemoved) {
- setVisibility(INVISIBLE);
+ if (animate && mRevealR > 0) {
+ Animator reveal = ViewAnimationUtils.createCircularReveal(
+ this, mRevealCx, mRevealCy, mRevealR, 0);
+ reveal.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+ reveal.setDuration(StackStateAnimator.ANIMATION_DURATION_CLOSE_REMOTE_INPUT);
+ reveal.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ setVisibility(INVISIBLE);
+ }
+ });
+ reveal.start();
+ } else {
+ setVisibility(INVISIBLE);
+ }
}
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_CLOSE,
mEntry.notification.getPackageName());
@@ -213,6 +246,17 @@
mEditText.setHint(mRemoteInput.getLabel());
}
+ public void focusAnimated() {
+ if (getVisibility() != VISIBLE) {
+ Animator animator = ViewAnimationUtils.createCircularReveal(
+ this, mRevealCx, mRevealCy, 0, mRevealR);
+ animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ animator.start();
+ }
+ focus();
+ }
+
public void focus() {
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_OPEN,
mEntry.notification.getPackageName());
@@ -243,7 +287,7 @@
mProgressBar.setVisibility(INVISIBLE);
mController.removeSpinning(mEntry.key);
updateSendButton();
- onDefocus();
+ onDefocus(false /* animate */);
}
private void updateSendButton() {
@@ -262,7 +306,7 @@
}
public void close() {
- mEditText.defocusIfNeeded();
+ mEditText.defocusIfNeeded(false /* animated */);
}
@Override
@@ -304,13 +348,14 @@
}
public boolean isActive() {
- return mEditText.isFocused();
+ return mEditText.isFocused() && mEditText.isEnabled();
}
public void stealFocusFrom(RemoteInputView other) {
other.close();
setPendingIntent(other.mPendingIntent);
setRemoteInput(other.mRemoteInputs, other.mRemoteInput);
+ setRevealParameters(other.mRevealCx, other.mRevealCy, other.mRevealR);
focus();
}
@@ -321,7 +366,6 @@
* @return true if a matching action was found, false otherwise
*/
public boolean updatePendingIntentFromActions(Notification.Action[] actions) {
- boolean found = false;
if (mPendingIntent == null || actions == null) {
return false;
}
@@ -364,6 +408,12 @@
mRemoved = true;
}
+ public void setRevealParameters(int cx, int cy, int r) {
+ mRevealCx = cx;
+ mRevealCy = cy;
+ mRevealR = r;
+ }
+
/**
* An EditText that changes appearance based on whether it's focusable and becomes
* un-focusable whenever the user navigates away from it or it becomes invisible.
@@ -379,14 +429,14 @@
mBackground = getBackground();
}
- private void defocusIfNeeded() {
+ private void defocusIfNeeded(boolean animate) {
if (mRemoteInputView != null && mRemoteInputView.mEntry.row.isChangingPosition()) {
return;
}
if (isFocusable() && isEnabled()) {
setInnerFocusable(false);
if (mRemoteInputView != null) {
- mRemoteInputView.onDefocus();
+ mRemoteInputView.onDefocus(animate);
}
mShowImeOnInputConnection = false;
}
@@ -397,7 +447,7 @@
super.onVisibilityChanged(changedView, visibility);
if (!isShown()) {
- defocusIfNeeded();
+ defocusIfNeeded(false /* animate */);
}
}
@@ -405,7 +455,7 @@
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
if (!focused) {
- defocusIfNeeded();
+ defocusIfNeeded(true /* animate */);
}
}
@@ -422,14 +472,21 @@
}
@Override
- public boolean onKeyPreIme(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
- defocusIfNeeded();
- final InputMethodManager imm = InputMethodManager.getInstance();
- imm.hideSoftInputFromWindow(getWindowToken(), 0);
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ // Eat the DOWN event here to prevent any default behavior.
return true;
}
- return super.onKeyPreIme(keyCode, event);
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ defocusIfNeeded(true /* animate */);
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
index a22f988..014afae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
@@ -23,6 +23,8 @@
String getProfileOwnerName();
boolean isVpnEnabled();
boolean isVpnRestricted();
+ /** Whether the VPN app should use branded VPN iconography. */
+ boolean isVpnBranded();
String getPrimaryVpnName();
String getProfileVpnName();
void onUserSwitched(int newUserId);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 5046456..07d3b59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -18,6 +18,8 @@
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.net.ConnectivityManager;
@@ -54,10 +56,13 @@
.build();
private static final int NO_NETWORK = -1;
+ private static final String VPN_BRANDED_META_DATA = "com.android.systemui.IS_BRANDED";
+
private final Context mContext;
private final ConnectivityManager mConnectivityManager;
private final IConnectivityManager mConnectivityManagerService;
private final DevicePolicyManager mDevicePolicyManager;
+ private final PackageManager mPackageManager;
private final UserManager mUserManager;
@GuardedBy("mCallbacks")
@@ -75,6 +80,7 @@
context.getSystemService(Context.CONNECTIVITY_SERVICE);
mConnectivityManagerService = IConnectivityManager.Stub.asInterface(
ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+ mPackageManager = context.getPackageManager();
mUserManager = (UserManager)
context.getSystemService(Context.USER_SERVICE);
@@ -165,6 +171,21 @@
}
@Override
+ public boolean isVpnBranded() {
+ VpnConfig cfg = mCurrentVpns.get(mVpnUserId);
+ if (cfg == null) {
+ return false;
+ }
+
+ String packageName = getPackageNameForVpnConfig(cfg);
+ if (packageName == null) {
+ return false;
+ }
+
+ return isVpnPackageBranded(packageName);
+ }
+
+ @Override
public void removeCallback(SecurityControllerCallback callback) {
synchronized (mCallbacks) {
if (callback == null) return;
@@ -245,6 +266,28 @@
mCurrentVpns = vpns;
}
+ private String getPackageNameForVpnConfig(VpnConfig cfg) {
+ if (cfg.legacy) {
+ return null;
+ }
+ return cfg.user;
+ }
+
+ private boolean isVpnPackageBranded(String packageName) {
+ boolean isBranded;
+ try {
+ ApplicationInfo info = mPackageManager.getApplicationInfo(packageName,
+ PackageManager.GET_META_DATA);
+ if (info == null || info.metaData == null || !info.isSystemApp()) {
+ return false;
+ }
+ isBranded = info.metaData.getBoolean(VPN_BRANDED_META_DATA, false);
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+ return isBranded;
+ }
+
private final NetworkCallback mNetworkCallback = new NetworkCallback() {
@Override
public void onAvailable(Network network) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
index 6ff8f77..ed8c7ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
@@ -182,6 +182,19 @@
static final int QS_DATA_4G = R.drawable.ic_qs_signal_4g;
+ static final int[][] DATA_4G_PLUS = {
+ { R.drawable.stat_sys_data_fully_connected_4g_plus,
+ R.drawable.stat_sys_data_fully_connected_4g_plus,
+ R.drawable.stat_sys_data_fully_connected_4g_plus,
+ R.drawable.stat_sys_data_fully_connected_4g_plus },
+ { R.drawable.stat_sys_data_fully_connected_4g_plus,
+ R.drawable.stat_sys_data_fully_connected_4g_plus,
+ R.drawable.stat_sys_data_fully_connected_4g_plus,
+ R.drawable.stat_sys_data_fully_connected_4g_plus }
+ };
+
+ static final int QS_DATA_4G_PLUS = R.drawable.ic_qs_signal_4g_plus;
+
// LTE branded "LTE"
static final int[][] DATA_LTE = {
{ R.drawable.stat_sys_data_fully_connected_lte,
@@ -195,15 +208,18 @@
};
static final int QS_DATA_LTE = R.drawable.ic_qs_signal_lte;
+ static final int QS_DATA_LTE_PLUS = R.drawable.ic_qs_signal_lte_plus;
static final int FLIGHT_MODE_ICON = R.drawable.stat_sys_airplane_mode;
static final int ROAMING_ICON = R.drawable.stat_sys_data_fully_connected_roam;
static final int ICON_LTE = R.drawable.stat_sys_data_fully_connected_lte;
+ static final int ICON_LTE_PLUS = R.drawable.stat_sys_data_fully_connected_lte_plus;
static final int ICON_G = R.drawable.stat_sys_data_fully_connected_g;
static final int ICON_E = R.drawable.stat_sys_data_fully_connected_e;
static final int ICON_H = R.drawable.stat_sys_data_fully_connected_h;
static final int ICON_3G = R.drawable.stat_sys_data_fully_connected_3g;
static final int ICON_4G = R.drawable.stat_sys_data_fully_connected_4g;
+ static final int ICON_4G_PLUS = R.drawable.stat_sys_data_fully_connected_4g_plus;
static final int ICON_1X = R.drawable.stat_sys_data_fully_connected_1x;
static final int ICON_CARRIER_NETWORK_CHANGE =
R.drawable.stat_sys_signal_carrier_network_change_animation;
@@ -213,6 +229,7 @@
static final int QS_ICON_LTE = R.drawable.ic_qs_signal_lte;
static final int QS_ICON_3G = R.drawable.ic_qs_signal_3g;
static final int QS_ICON_4G = R.drawable.ic_qs_signal_4g;
+ static final int QS_ICON_4G_PLUS = R.drawable.ic_qs_signal_4g_plus;
static final int QS_ICON_1X = R.drawable.ic_qs_signal_1x;
static final int QS_ICON_CARRIER_NETWORK_CHANGE =
R.drawable.ic_qs_signal_carrier_network_change_animation;
@@ -348,6 +365,21 @@
TelephonyIcons.QS_DATA_4G
);
+ static final MobileIconGroup FOUR_G_PLUS = new MobileIconGroup(
+ "4G+",
+ TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH,
+ TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
+ 0,0,
+ TelephonyIcons.TELEPHONY_NO_NETWORK,
+ TelephonyIcons.QS_TELEPHONY_NO_NETWORK,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
+ R.string.accessibility_data_connection_4g_plus,
+ TelephonyIcons.ICON_4G_PLUS,
+ true,
+ TelephonyIcons.QS_DATA_4G_PLUS
+ );
+
static final MobileIconGroup LTE = new MobileIconGroup(
"LTE",
TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH,
@@ -363,6 +395,21 @@
TelephonyIcons.QS_DATA_LTE
);
+ static final MobileIconGroup LTE_PLUS = new MobileIconGroup(
+ "LTE+",
+ TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH,
+ TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
+ 0, 0,
+ TelephonyIcons.TELEPHONY_NO_NETWORK,
+ TelephonyIcons.QS_TELEPHONY_NO_NETWORK,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
+ R.string.accessibility_data_connection_lte_plus,
+ TelephonyIcons.ICON_LTE_PLUS,
+ true,
+ TelephonyIcons.QS_DATA_LTE_PLUS
+ );
+
static final MobileIconGroup ROAMING = new MobileIconGroup(
"Roaming",
TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 89d2712..27ba003 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -381,6 +381,18 @@
Log.e(TAG, "Couldn't switch to user, id=" + userId);
}
+ public int getSwitchableUserCount() {
+ int count = 0;
+ final int N = mUsers.size();
+ for (int i = 0; i < N; ++i) {
+ UserRecord record = mUsers.get(i);
+ if (record.info != null && record.info.supportsSwitchTo()) {
+ count++;
+ }
+ }
+ return count;
+ }
+
private void switchToUserId(int id) {
try {
pauseRefreshUsers();
@@ -419,23 +431,27 @@
}
private void listenForCallState() {
- TelephonyManager.from(mContext).listen(new PhoneStateListener() {
- private int mCallState;
- @Override
- public void onCallStateChanged(int state, String incomingNumber) {
- if (mCallState == state) return;
- if (DEBUG) Log.v(TAG, "Call state changed: " + state);
- mCallState = state;
- int currentUserId = ActivityManager.getCurrentUser();
- UserInfo userInfo = mUserManager.getUserInfo(currentUserId);
- if (userInfo != null && userInfo.isGuest()) {
- showGuestNotification(currentUserId);
- }
- refreshUsers(UserHandle.USER_NULL);
- }
- }, PhoneStateListener.LISTEN_CALL_STATE);
+ TelephonyManager.from(mContext).listen(mPhoneStateListener,
+ PhoneStateListener.LISTEN_CALL_STATE);
}
+ private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ private int mCallState;
+
+ @Override
+ public void onCallStateChanged(int state, String incomingNumber) {
+ if (mCallState == state) return;
+ if (DEBUG) Log.v(TAG, "Call state changed: " + state);
+ mCallState = state;
+ int currentUserId = ActivityManager.getCurrentUser();
+ UserInfo userInfo = mUserManager.getUserInfo(currentUserId);
+ if (userInfo != null && userInfo.isGuest()) {
+ showGuestNotification(currentUserId);
+ }
+ refreshUsers(UserHandle.USER_NULL);
+ }
+ };
+
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 1e5654e..c8c4310 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -412,7 +412,10 @@
@Override
protected void onDraw(Canvas canvas) {
- canvas.drawRect(0, mCurrentBounds.top, getWidth(), mCurrentBounds.bottom, mBackgroundPaint);
+ if (mCurrentBounds.top < mCurrentBounds.bottom) {
+ canvas.drawRect(0, mCurrentBounds.top, getWidth(), mCurrentBounds.bottom,
+ mBackgroundPaint);
+ }
if (DEBUG) {
int y = mTopPadding;
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
@@ -668,30 +671,74 @@
public void setStackHeight(float height) {
mLastSetStackHeight = height;
setIsExpanded(height > 0.0f);
- int newStackHeight = (int) height;
- int minStackHeight = getLayoutMinHeight();
int stackHeight;
- float paddingOffset;
- boolean trackingHeadsUp = mTrackingHeadsUp || mHeadsUpManager.hasPinnedHeadsUp();
- int normalUnfoldPositionStart = trackingHeadsUp
- ? mHeadsUpManager.getTopHeadsUpPinnedHeight()
- : minStackHeight;
- if (newStackHeight - mTopPadding - mTopPaddingOverflow >= normalUnfoldPositionStart
- || getNotGoneChildCount() == 0) {
- paddingOffset = mTopPaddingOverflow;
- stackHeight = newStackHeight;
+ float translationY;
+ float appearEndPosition = getAppearEndPosition();
+ float appearStartPosition = getAppearStartPosition();
+ if (height >= appearEndPosition) {
+ translationY = mTopPaddingOverflow;
+ stackHeight = (int) height;
} else {
- int translationY;
- translationY = newStackHeight - normalUnfoldPositionStart;
- paddingOffset = translationY - mTopPadding;
- stackHeight = (int) (height - (translationY - mTopPadding));
+ float appearFraction = getAppearFraction(height);
+ if (appearFraction >= 0) {
+ translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
+ appearFraction);
+ } else {
+ // This may happen when pushing up a heads up. We linearly push it up from the
+ // start
+ translationY = height - appearStartPosition + getExpandTranslationStart();
+ }
+ stackHeight = (int) (height - translationY);
}
if (stackHeight != mCurrentStackHeight) {
mCurrentStackHeight = stackHeight;
updateAlgorithmHeightAndPadding();
requestChildrenUpdate();
}
- setStackTranslation(paddingOffset);
+ setStackTranslation(translationY);
+ }
+
+ /**
+ * @return The translation at the beginning when expanding.
+ * Measured relative to the resting position.
+ */
+ private float getExpandTranslationStart() {
+ int startPosition = mTrackingHeadsUp || mHeadsUpManager.hasPinnedHeadsUp()
+ ? 0 : -getFirstChildIntrinsicHeight();
+ return startPosition - mTopPadding;
+ }
+
+ /**
+ * @return the position from where the appear transition starts when expanding.
+ * Measured in absolute height.
+ */
+ private float getAppearStartPosition() {
+ return mTrackingHeadsUp
+ ? mHeadsUpManager.getTopHeadsUpPinnedHeight()
+ : 0;
+ }
+
+ /**
+ * @return the position from where the appear transition ends when expanding.
+ * Measured in absolute height.
+ */
+ private float getAppearEndPosition() {
+ int firstItemHeight = mTrackingHeadsUp || mHeadsUpManager.hasPinnedHeadsUp()
+ ? mHeadsUpManager.getTopHeadsUpPinnedHeight() + mBottomStackPeekSize
+ + mBottomStackSlowDownHeight
+ : getLayoutMinHeight();
+ return firstItemHeight + mTopPadding + mTopPaddingOverflow;
+ }
+
+ /**
+ * @param height the height of the panel
+ * @return the fraction of the appear animation that has been performed
+ */
+ public float getAppearFraction(float height) {
+ float appearEndPosition = getAppearEndPosition();
+ float appearStartPosition = getAppearStartPosition();
+ return (height - appearStartPosition)
+ / (appearEndPosition - appearStartPosition);
}
public float getStackTranslation() {
@@ -1965,15 +2012,16 @@
bottom = Math.min(bottom, getHeight());
}
} else {
- top = (int) (mTopPadding + mStackTranslation);
+ top = mTopPadding;
bottom = top;
}
if (mPhoneStatusBar.getBarState() != StatusBarState.KEYGUARD) {
- mBackgroundBounds.top = (int) Math.max(mTopPadding + mStackTranslation, top);
+ top = (int) Math.max(mTopPadding + mStackTranslation, top);
} else {
// otherwise the animation from the shade to the keyguard will jump as it's maxed
- mBackgroundBounds.top = Math.max(0, top);
+ top = Math.max(0, top);
}
+ mBackgroundBounds.top = top;
mBackgroundBounds.bottom = Math.min(getHeight(), Math.max(bottom, top));
}
@@ -2096,17 +2144,22 @@
}
public int getLayoutMinHeight() {
+ int firstChildMinHeight = getFirstChildIntrinsicHeight();
+ return Math.min(firstChildMinHeight + mBottomStackPeekSize + mBottomStackSlowDownHeight,
+ mMaxLayoutHeight - mTopPadding);
+ }
+
+ public int getFirstChildIntrinsicHeight() {
final ExpandableView firstChild = getFirstChildNotGone();
int firstChildMinHeight = firstChild != null
? firstChild.getIntrinsicHeight()
: mEmptyShadeView != null
- ? mEmptyShadeView.getMinHeight()
+ ? mEmptyShadeView.getIntrinsicHeight()
: mCollapsedSize;
if (mOwnScrollY > 0) {
firstChildMinHeight = Math.max(firstChildMinHeight - mOwnScrollY, mCollapsedSize);
}
- return Math.min(firstChildMinHeight + mBottomStackPeekSize + mBottomStackSlowDownHeight,
- mMaxLayoutHeight - mTopPadding);
+ return firstChildMinHeight;
}
public float getTopPaddingOverflow() {
@@ -3402,7 +3455,7 @@
notifyHeightChangeListener(mEmptyShadeView);
}
};
- if (mAnimationsEnabled) {
+ if (mAnimationsEnabled && mIsExpanded) {
mEmptyShadeView.setWillBeGone(true);
mEmptyShadeView.performVisibilityAnimation(false, onFinishedRunnable);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 659eaf7..3804b42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -44,6 +44,7 @@
public static final int ANIMATION_DURATION_GO_TO_FULL_SHADE = 448;
public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
+ public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150;
public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 650;
public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 230;
public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 27726af..2d4900b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -257,4 +257,9 @@
} catch (RemoteException ex) {
}
}
+
+ @Override
+ public void handleSystemNavigationKey(int arg1) {
+ // Not implemented
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
index 8881c79..6e08139 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
@@ -47,9 +47,9 @@
@Override
public void onAttached() {
super.onAttached();
- TunerService.get(getContext()).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
mHasPercentage = Settings.System.getInt(getContext().getContentResolver(),
SHOW_PERCENT_SETTING, 0) != 0;
+ TunerService.get(getContext()).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/CalibratePreference.java b/packages/SystemUI/src/com/android/systemui/tuner/CalibratePreference.java
deleted file mode 100644
index ff7be13..0000000
--- a/packages/SystemUI/src/com/android/systemui/tuner/CalibratePreference.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.tuner;
-
-import android.content.Context;
-import android.support.v7.preference.DialogPreference;
-import android.util.AttributeSet;
-
-public class CalibratePreference extends DialogPreference {
- public CalibratePreference(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java
index ea92443..caa0527 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java
@@ -32,6 +32,8 @@
private boolean mHasSeconds;
private ArraySet<String> mBlacklist;
private boolean mHasSetValue;
+ private boolean mReceivedSeconds;
+ private boolean mReceivedClock;
public ClockPreference(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -55,12 +57,14 @@
@Override
public void onTuningChanged(String key, String newValue) {
if (StatusBarIconController.ICON_BLACKLIST.equals(key)) {
+ mReceivedClock = true;
mBlacklist = StatusBarIconController.getIconBlacklist(newValue);
mClockEnabled = !mBlacklist.contains(mClock);
} else if (Clock.CLOCK_SECONDS.equals(key)) {
+ mReceivedSeconds = true;
mHasSeconds = newValue != null && Integer.parseInt(newValue) != 0;
}
- if (!mHasSetValue) {
+ if (!mHasSetValue && mReceivedClock && mReceivedSeconds) {
// Because of the complicated tri-state it can end up looping and setting state back to
// what the user didn't choose. To avoid this, just set the state once and rely on the
// preference to handle updates.
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ColorAndAppearanceFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/ColorAndAppearanceFragment.java
deleted file mode 100644
index af95cf9..0000000
--- a/packages/SystemUI/src/com/android/systemui/tuner/ColorAndAppearanceFragment.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.tuner;
-
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.provider.Settings.Secure;
-import android.support.v14.preference.PreferenceFragment;
-import android.support.v7.preference.Preference;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.SeekBar;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.NightModeController;
-
-public class ColorAndAppearanceFragment extends PreferenceFragment {
-
- private static final String KEY_CALIBRATE = "calibrate";
-
- private static final long RESET_DELAY = 10000;
- private static final CharSequence KEY_NIGHT_MODE = "night_mode";
-
- private NightModeController mNightModeController;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mNightModeController = new NightModeController(getContext());
- }
-
- @Override
- public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
- addPreferencesFromResource(R.xml.color_and_appearance);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- MetricsLogger.visibility(getContext(), MetricsEvent.TUNER_COLOR_AND_APPEARANCE, true);
- // TODO: Figure out better title model for Tuner, to avoid any more of this.
- getActivity().setTitle(R.string.color_and_appearance);
-
- Preference nightMode = findPreference(KEY_NIGHT_MODE);
- nightMode.setSummary(mNightModeController.isEnabled()
- ? R.string.night_mode_on : R.string.night_mode_off);
- }
-
- @Override
- public void onPause() {
- super.onPause();
- MetricsLogger.visibility(getContext(), MetricsEvent.TUNER_COLOR_AND_APPEARANCE, false);
- }
-
- @Override
- public void onDisplayPreferenceDialog(Preference preference) {
- if (preference instanceof CalibratePreference) {
- CalibrateDialog.show(this);
- } else {
- super.onDisplayPreferenceDialog(preference);
- }
- }
-
- private void startRevertTimer() {
- getView().postDelayed(mResetColorMatrix, RESET_DELAY);
- }
-
- private void onApply() {
- MetricsLogger.action(getContext(), MetricsEvent.ACTION_TUNER_CALIBRATE_DISPLAY_CHANGED);
- mNightModeController.setCustomValues(Settings.Secure.getString(
- getContext().getContentResolver(), Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX));
- getView().removeCallbacks(mResetColorMatrix);
- }
-
- private void onRevert() {
- getView().removeCallbacks(mResetColorMatrix);
- mResetColorMatrix.run();
- }
-
- private final Runnable mResetColorMatrix = new Runnable() {
- @Override
- public void run() {
- ((DialogFragment) getFragmentManager().findFragmentByTag("RevertWarning")).dismiss();
- Settings.Secure.putString(getContext().getContentResolver(),
- Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, null);
- }
- };
-
- public static class CalibrateDialog extends DialogFragment implements
- DialogInterface.OnClickListener {
- private float[] mValues;
- private NightModeController mNightModeController;
-
- public static void show(ColorAndAppearanceFragment fragment) {
- CalibrateDialog dialog = new CalibrateDialog();
- dialog.setTargetFragment(fragment, 0);
- dialog.show(fragment.getFragmentManager(), "Calibrate");
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mNightModeController = new NightModeController(getContext());
- String customValues = mNightModeController.getCustomValues();
- if (customValues == null) {
- // Generate this as a string because its the easiest way to generate a copy of the
- // identity.
- customValues = NightModeController.toString(NightModeController.IDENTITY_MATRIX);
- }
- mValues = NightModeController.toValues(customValues);
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- View v = LayoutInflater.from(getContext()).inflate(R.layout.calibrate_sliders, null);
- bindView(v.findViewById(R.id.r_group), 0);
- bindView(v.findViewById(R.id.g_group), 5);
- bindView(v.findViewById(R.id.b_group), 10);
- MetricsLogger.visible(getContext(), MetricsEvent.TUNER_CALIBRATE_DISPLAY);
- return new AlertDialog.Builder(getContext())
- .setTitle(R.string.calibrate_display)
- .setView(v)
- .setPositiveButton(R.string.color_apply, this)
- .setNegativeButton(android.R.string.cancel, null)
- .create();
- }
-
- @Override
- public void onDismiss(DialogInterface dialog) {
- super.onDismiss(dialog);
- MetricsLogger.hidden(getContext(), MetricsEvent.TUNER_CALIBRATE_DISPLAY);
- }
-
- private void bindView(View view, final int index) {
- SeekBar seekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar);
- seekBar.setMax(1000);
- seekBar.setProgress((int) (1000 * mValues[index]));
- seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- mValues[index] = progress / 1000f;
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
- });
- }
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (mValues[0] == 1 && mValues[5] == 1 && mValues[10] == 1) {
- // Allow removal of matrix by all values set to highest.
- mNightModeController.setCustomValues(null);
- return;
- }
- ((ColorAndAppearanceFragment) getTargetFragment()).startRevertTimer();
- Settings.Secure.putString(getContext().getContentResolver(),
- Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX,
- NightModeController.toString(mValues));
- RevertWarning.show((ColorAndAppearanceFragment) getTargetFragment());
- }
- }
-
- public static class RevertWarning extends DialogFragment
- implements DialogInterface.OnClickListener {
-
- public static void show(ColorAndAppearanceFragment fragment) {
- RevertWarning warning = new RevertWarning();
- warning.setTargetFragment(fragment, 0);
- warning.show(fragment.getFragmentManager(), "RevertWarning");
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- AlertDialog alertDialog = new AlertDialog.Builder(getContext())
- .setTitle(R.string.color_revert_title)
- .setMessage(R.string.color_revert_message)
- .setPositiveButton(R.string.ok, this)
- .create();
- alertDialog.setCanceledOnTouchOutside(true);
- return alertDialog;
- }
-
- @Override
- public void onCancel(DialogInterface dialog) {
- super.onCancel(dialog);
- ((ColorAndAppearanceFragment) getTargetFragment()).onRevert();
- }
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- ((ColorAndAppearanceFragment) getTargetFragment()).onApply();
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/NightModeFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/NightModeFragment.java
deleted file mode 100644
index ae2856c..0000000
--- a/packages/SystemUI/src/com/android/systemui/tuner/NightModeFragment.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.tuner;
-
-import android.annotation.Nullable;
-import android.app.UiModeManager;
-import android.content.Context;
-import android.os.Bundle;
-import android.provider.Settings.Secure;
-import android.support.v14.preference.PreferenceFragment;
-import android.support.v14.preference.SwitchPreference;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.Preference.OnPreferenceChangeListener;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Switch;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.NightModeController;
-import com.android.systemui.statusbar.policy.NightModeController.Listener;
-import com.android.systemui.tuner.TunerService.Tunable;
-
-public class NightModeFragment extends PreferenceFragment implements Tunable,
- Listener, OnPreferenceChangeListener {
-
- private static final String TAG = "NightModeFragment";
-
- public static final String EXTRA_SHOW_NIGHT_MODE = "show_night_mode";
-
- private static final CharSequence KEY_AUTO = "auto";
- private static final CharSequence KEY_ADJUST_TINT = "adjust_tint";
- private static final CharSequence KEY_ADJUST_BRIGHTNESS = "adjust_brightness";
-
- private Switch mSwitch;
-
- private NightModeController mNightModeController;
- private SwitchPreference mAutoSwitch;
- private SwitchPreference mAdjustTint;
- private SwitchPreference mAdjustBrightness;
- private UiModeManager mUiModeManager;
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mNightModeController = new NightModeController(getContext());
- mUiModeManager = getContext().getSystemService(UiModeManager.class);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- final View view = LayoutInflater.from(getContext()).inflate(
- R.layout.night_mode_settings, container, false);
- ((ViewGroup) view).addView(super.onCreateView(inflater, container, savedInstanceState));
- return view;
- }
-
- @Override
- public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
- final Context context = getPreferenceManager().getContext();
-
- addPreferencesFromResource(R.xml.night_mode);
- mAutoSwitch = (SwitchPreference) findPreference(KEY_AUTO);
- mAutoSwitch.setOnPreferenceChangeListener(this);
- mAdjustTint = (SwitchPreference) findPreference(KEY_ADJUST_TINT);
- mAdjustTint.setOnPreferenceChangeListener(this);
- mAdjustBrightness = (SwitchPreference) findPreference(KEY_ADJUST_BRIGHTNESS);
- mAdjustBrightness.setOnPreferenceChangeListener(this);
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- View switchBar = view.findViewById(R.id.switch_bar);
- mSwitch = (Switch) switchBar.findViewById(android.R.id.switch_widget);
- mSwitch.setChecked(mNightModeController.isEnabled());
- switchBar.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- boolean newState = !mNightModeController.isEnabled();
- MetricsLogger.action(getContext(), MetricsEvent.ACTION_TUNER_NIGHT_MODE, newState);
- mNightModeController.setNightMode(newState);
- mSwitch.setChecked(newState);
- }
- });
- }
-
- @Override
- public void onResume() {
- super.onResume();
- MetricsLogger.visibility(getContext(), MetricsEvent.TUNER_NIGHT_MODE, true);
- mNightModeController.addListener(this);
- TunerService.get(getContext()).addTunable(this, Secure.BRIGHTNESS_USE_TWILIGHT,
- NightModeController.NIGHT_MODE_ADJUST_TINT);
- calculateDisabled();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- MetricsLogger.visibility(getContext(), MetricsEvent.TUNER_NIGHT_MODE, false);
- mNightModeController.removeListener(this);
- TunerService.get(getContext()).removeTunable(this);
- }
-
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- final Boolean value = (Boolean) newValue;
- if (mAutoSwitch == preference) {
- MetricsLogger.action(getContext(), MetricsEvent.ACTION_TUNER_NIGHT_MODE_AUTO, value);
- mNightModeController.setAuto(value);
- } else if (mAdjustTint == preference) {
- MetricsLogger.action(getContext(),
- MetricsEvent.ACTION_TUNER_NIGHT_MODE_ADJUST_TINT, value);
- mNightModeController.setAdjustTint(value);
- postCalculateDisabled();
- } else if (mAdjustBrightness == preference) {
- MetricsLogger.action(getContext(),
- MetricsEvent.ACTION_TUNER_NIGHT_MODE_ADJUST_BRIGHTNESS, value);
- TunerService.get(getContext()).setValue(Secure.BRIGHTNESS_USE_TWILIGHT,
- value ? 1 : 0);
- postCalculateDisabled();
- } else {
- return false;
- }
- return true;
- }
-
- private void postCalculateDisabled() {
- // Post this because its the easiest way to wait for all state to be calculated.
- getView().post(new Runnable() {
- @Override
- public void run() {
- calculateDisabled();
- }
- });
- }
-
- private void calculateDisabled() {
- int enabledCount = (mAdjustTint.isChecked() ? 1 : 0)
- + (mAdjustBrightness.isChecked() ? 1 : 0);
- if (enabledCount == 1) {
- if (mAdjustTint.isChecked()) {
- mAdjustTint.setEnabled(false);
- } else {
- mAdjustBrightness.setEnabled(false);
- }
- } else {
- mAdjustTint.setEnabled(true);
- mAdjustBrightness.setEnabled(true);
- }
- }
-
- @Override
- public void onTuningChanged(String key, String newValue) {
- if (Secure.BRIGHTNESS_USE_TWILIGHT.equals(key)) {
- mAdjustBrightness.setChecked(newValue != null && Integer.parseInt(newValue) != 0);
- } else if (NightModeController.NIGHT_MODE_ADJUST_TINT.equals(key)) {
- // Default on.
- mAdjustTint.setChecked(newValue == null || Integer.parseInt(newValue) != 0);
- }
- }
-
- @Override
- public void onNightModeChanged() {
- mSwitch.setChecked(mNightModeController.isEnabled());
- }
-
- @Override
- public void onTwilightAutoChanged() {
- mAutoSwitch.setChecked(mNightModeController.isAuto());
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java b/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java
deleted file mode 100644
index fe44502..0000000
--- a/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.tuner;
-
-import android.content.Intent;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
-import com.android.systemui.Prefs;
-import com.android.systemui.Prefs.Key;
-import com.android.systemui.R;
-import com.android.systemui.qs.QSTile;
-import com.android.systemui.statusbar.policy.NightModeController;
-
-
-public class NightModeTile extends QSTile<QSTile.State> implements NightModeController.Listener {
-
- public static final String NIGHT_MODE_SPEC = "night";
-
- private final NightModeController mNightModeController;
-
- private int mIndex;
- private String mCurrentValue;
-
- private boolean mCustomEnabled;
- private String[] mValues;
- private CharSequence[] mValueTitles;
-
- public NightModeTile(Host host) {
- super(host);
- mNightModeController = host.getNightModeController();
- }
-
- @Override
- public boolean isAvailable() {
- return Prefs.getBoolean(mContext, Key.QS_NIGHT_ADDED, false)
- && TunerService.isTunerEnabled(mContext);
- }
-
- @Override
- public void setListening(boolean listening) {
- if (listening) {
- mNightModeController.addListener(this);
- refreshState();
- } else {
- mNightModeController.removeListener(this);
- }
- }
-
- @Override
- public State newTileState() {
- return new State();
- }
-
- @Override
- public Intent getLongClickIntent() {
- return new Intent(mContext, TunerActivity.class)
- .putExtra(NightModeFragment.EXTRA_SHOW_NIGHT_MODE, true);
- }
-
- @Override
- protected void handleClick() {
- mNightModeController.setNightMode(!mNightModeController.isEnabled());
- refreshState();
- }
-
- @Override
- public CharSequence getTileLabel() {
- return mContext.getString(R.string.night_mode);
- }
-
- @Override
- protected void handleUpdateState(State state, Object arg) {
- // TODO: Right now this is just a dropper, needs an actual night icon.
- boolean enabled = mNightModeController.isEnabled();
- state.icon = ResourceIcon.get(enabled ? R.drawable.ic_night_mode
- : R.drawable.ic_night_mode_disabled);
- state.label = mContext.getString(R.string.night_mode);
- state.contentDescription = mContext.getString(R.string.night_mode);
- }
-
- @Override
- public void onNightModeChanged() {
- refreshState();
- }
-
- @Override
- public void onTwilightAutoChanged() {
- // Don't care.
- }
-
- @Override
- public int getMetricsCategory() {
- return MetricsEvent.QS_COLOR_MATRIX;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
index 5e5da74..5fe9296 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
@@ -39,10 +39,7 @@
final String action = getIntent().getAction();
boolean showDemoMode = action != null && action.equals(
"com.android.settings.action.DEMO_MODE");
- boolean showNightMode = getIntent().getBooleanExtra(
- NightModeFragment.EXTRA_SHOW_NIGHT_MODE, false);
- final PreferenceFragment fragment = showNightMode ? new NightModeFragment()
- : showDemoMode ? new DemoModeFragment()
+ final PreferenceFragment fragment = showDemoMode ? new DemoModeFragment()
: new TunerFragment();
getFragmentManager().beginTransaction().replace(R.id.content_frame,
fragment, TAG_TUNER).commit();
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerZenModePanel.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerZenModePanel.java
index cc0ffb0..1ea23bb 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerZenModePanel.java
@@ -54,7 +54,6 @@
mHeaderSwitch = findViewById(R.id.tuner_zen_switch);
mHeaderSwitch.setVisibility(View.VISIBLE);
mHeaderSwitch.setOnClickListener(this);
- mHeaderSwitch.findViewById(com.android.internal.R.id.up).setVisibility(View.GONE);
((TextView) mHeaderSwitch.findViewById(android.R.id.title)).setText(
R.string.quick_settings_dnd_label);
mZenModePanel = (ZenModePanel) findViewById(R.id.zen_mode_panel);
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
index 30622d2..5e4854c 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -196,7 +196,23 @@
}
mInitialized = true;
mContext = context;
- Resources res = context.getResources();
+
+ mActivityManager = ActivityManagerNative.getDefault();
+ SystemServicesProxy.getInstance(context).registerTaskStackListener(mTaskStackListener);
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter);
+ mOnboardingShown = Prefs.getBoolean(
+ mContext, TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN, false);
+
+ loadConfigurationsAndApply();
+ mPipRecentsOverlayManager = new PipRecentsOverlayManager(context);
+ mMediaSessionManager =
+ (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
+ }
+
+ private void loadConfigurationsAndApply() {
+ Resources res = mContext.getResources();
mDefaultPipBounds = Rect.unflattenFromString(res.getString(
com.android.internal.R.string.config_defaultPictureInPictureBounds));
mSettingsPipBounds = Rect.unflattenFromString(res.getString(
@@ -209,25 +225,19 @@
R.string.pip_recents_focused_bounds));
mRecentsFocusChangedAnimationDurationMs = res.getInteger(
R.integer.recents_tv_pip_focus_anim_duration);
- mPipBounds = mDefaultPipBounds;
- mActivityManager = ActivityManagerNative.getDefault();
- SystemServicesProxy.getInstance(context).registerTaskStackListener(mTaskStackListener);
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
- mContext.registerReceiver(mBroadcastReceiver, intentFilter);
- mOnboardingShown = Prefs.getBoolean(
- mContext, TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN, false);
-
- mPipRecentsOverlayManager = new PipRecentsOverlayManager(context);
- mMediaSessionManager =
- (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
+ // Reset the PIP bounds and apply. PIP bounds can be changed by two reasons.
+ // 1. Configuration changed due to the language change (RTL <-> RTL)
+ // 2. SystemUI restarts after the crash
+ mPipBounds = isSettingsShown() ? mSettingsPipBounds : mDefaultPipBounds;
+ resizePinnedStack(getPinnedStackInfo() == null ? STATE_NO_PIP : STATE_PIP_OVERLAY);
}
/**
* Updates the PIP per configuration changed.
*/
void onConfigurationChanged() {
+ loadConfigurationsAndApply();
mPipRecentsOverlayManager.onConfigurationChanged(mContext);
}
@@ -443,6 +453,16 @@
return mState != STATE_NO_PIP;
}
+ private StackInfo getPinnedStackInfo() {
+ StackInfo stackInfo = null;
+ try {
+ stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ } catch (RemoteException e) {
+ Log.e(TAG, "getStackInfo failed", e);
+ }
+ return stackInfo;
+ }
+
private void handleMediaResourceGranted(String[] packageNames) {
if (mState == STATE_NO_PIP) {
mLastPackagesResourceGranted = packageNames;
@@ -525,10 +545,21 @@
return PLAYBACK_STATE_UNAVAILABLE;
}
- private static boolean isSettingsShown(ComponentName topActivity) {
+ private boolean isSettingsShown() {
+ List<RunningTaskInfo> runningTasks;
+ try {
+ runningTasks = mActivityManager.getTasks(1, 0);
+ if (runningTasks == null || runningTasks.size() == 0) {
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.d(TAG, "Failed to detect top activity", e);
+ return false;
+ }
+ ComponentName topActivity = runningTasks.get(0).topActivity;
for (Pair<String, String> componentName : sSettingsPackageAndClassNamePairList) {
String packageName = componentName.first;
- if (topActivity.getPackageName().equals(componentName.first)) {
+ if (topActivity.getPackageName().equals(packageName)) {
String className = componentName.second;
if (className == null || topActivity.getClassName().equals(className)) {
return true;
@@ -544,16 +575,10 @@
if (mState != STATE_NO_PIP) {
boolean hasPip = false;
- StackInfo stackInfo = null;
- try {
- stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
- if (stackInfo == null) {
- Log.w(TAG, "There is no pinned stack");
- closePipInternal(false);
- return;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "getStackInfo failed", e);
+ StackInfo stackInfo = getPinnedStackInfo();
+ if (stackInfo == null || stackInfo.taskIds == null) {
+ Log.w(TAG, "There is nothing in pinned stack");
+ closePipInternal(false);
return;
}
for (int i = stackInfo.taskIds.length - 1; i >= 0; --i) {
@@ -570,20 +595,10 @@
}
}
if (mState == STATE_PIP_OVERLAY) {
- try {
- List<RunningTaskInfo> runningTasks = mActivityManager.getTasks(1, 0);
- if (runningTasks == null || runningTasks.size() == 0) {
- return;
- }
- RunningTaskInfo topTask = runningTasks.get(0);
- Rect bounds = isSettingsShown(topTask.topActivity)
- ? mSettingsPipBounds : mDefaultPipBounds;
- if (mPipBounds != bounds) {
- mPipBounds = bounds;
- resizePinnedStack(STATE_PIP_OVERLAY);
- }
- } catch (RemoteException e) {
- Log.d(TAG, "Failed to detect top activity", e);
+ Rect bounds = isSettingsShown() ? mSettingsPipBounds : mDefaultPipBounds;
+ if (mPipBounds != bounds) {
+ mPipBounds = bounds;
+ resizePinnedStack(STATE_PIP_OVERLAY);
}
}
}
@@ -591,15 +606,9 @@
@Override
public void onActivityPinned() {
if (DEBUG) Log.d(TAG, "onActivityPinned()");
- StackInfo stackInfo = null;
- try {
- stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
- if (stackInfo == null) {
- Log.w(TAG, "Cannot find pinned stack");
- return;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "getStackInfo failed", e);
+ StackInfo stackInfo = getPinnedStackInfo();
+ if (stackInfo == null) {
+ Log.w(TAG, "Cannot find pinned stack");
return;
}
if (DEBUG) Log.d(TAG, "PINNED_STACK:" + stackInfo);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 1973e05..4aa14e2 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -17,9 +17,7 @@
package com.android.systemui.volume;
import android.accessibilityservice.AccessibilityServiceInfo;
-import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.app.Dialog;
@@ -43,9 +41,11 @@
import android.os.Message;
import android.os.SystemClock;
import android.provider.Settings.Global;
+import android.transition.AutoTransition;
+import android.transition.Transition;
+import android.transition.TransitionManager;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.Slog;
import android.util.SparseBooleanArray;
import android.view.Gravity;
import android.view.MotionEvent;
@@ -63,11 +63,12 @@
import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageButton;
-import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
+import com.android.settingslib.Utils;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.tuner.TunerService;
@@ -101,8 +102,10 @@
private final H mHandler = new H();
private final VolumeDialogController mController;
+ private Window mWindow;
private CustomDialog mDialog;
private ViewGroup mDialogView;
+ private ViewGroup mDialogRowsView;
private ViewGroup mDialogContentView;
private ImageButton mExpandButton;
private final List<VolumeRow> mRows = new ArrayList<>();
@@ -113,7 +116,6 @@
private final AccessibilityManager mAccessibilityMgr;
private int mExpandButtonAnimationDuration;
private ZenFooter mZenFooter;
- private LayoutTransition mLayoutTransition;
private final Object mSafetyWarningLock = new Object();
private final Accessibility mAccessibility = new Accessibility();
private final ColorStateList mActiveSliderTint;
@@ -151,8 +153,9 @@
mZenModeController = zenModeController;
mKeyguard = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- mAccessibilityMgr = (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
- mActiveSliderTint = loadColorStateList(R.color.system_accent_color);
+ mAccessibilityMgr =
+ (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ mActiveSliderTint = ColorStateList.valueOf(Utils.getColorAccent(mContext));
mInactiveSliderTint = loadColorStateList(R.color.volume_slider_inactive);
initDialog();
@@ -171,15 +174,13 @@
mDialog = new CustomDialog(mContext);
mSpTexts = new SpTexts(mContext);
- mLayoutTransition = new LayoutTransition();
- mLayoutTransition.setDuration(new ValueAnimator().getDuration() / 2);
mHovering = false;
mShowing = false;
- final Window window = mDialog.getWindow();
- window.requestFeature(Window.FEATURE_NO_TITLE);
- window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
- window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
- window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ mWindow = mDialog.getWindow();
+ mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+ mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+ mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
@@ -187,7 +188,7 @@
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
mDialog.setCanceledOnTouchOutside(true);
final Resources res = mContext.getResources();
- final WindowManager.LayoutParams lp = window.getAttributes();
+ final WindowManager.LayoutParams lp = mWindow.getAttributes();
lp.type = mWindowType;
lp.format = PixelFormat.TRANSLUCENT;
lp.setTitle(VolumeDialog.class.getSimpleName());
@@ -195,9 +196,8 @@
lp.y = res.getDimensionPixelSize(R.dimen.volume_offset_top);
lp.gravity = Gravity.TOP;
lp.windowAnimations = -1;
- window.setAttributes(lp);
- window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
-
+ mWindow.setAttributes(lp);
+ mWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
mDialog.setContentView(R.layout.volume_dialog);
mDialogView = (ViewGroup) mDialog.findViewById(R.id.volume_dialog);
@@ -212,13 +212,13 @@
}
});
mDialogContentView = (ViewGroup) mDialog.findViewById(R.id.volume_dialog_content);
+ mDialogRowsView = (ViewGroup) mDialogContentView.findViewById(R.id.volume_dialog_rows);
mExpanded = false;
mExpandButton = (ImageButton) mDialogView.findViewById(R.id.volume_expand_button);
mExpandButton.setOnClickListener(mClickExpand);
updateWindowWidthH();
updateExpandButtonH();
- mDialogContentView.setLayoutTransition(mLayoutTransition);
mMotion = new VolumeDialogMotion(mDialog, mDialogView, mDialogContentView, mExpandButton,
new VolumeDialogMotion.Callback() {
@Override
@@ -309,10 +309,7 @@
private void addRow(int stream, int iconRes, int iconMuteRes, boolean important) {
VolumeRow row = new VolumeRow();
initRow(row, stream, iconRes, iconMuteRes, important);
- if (!mRows.isEmpty()) {
- addSpacer(row);
- }
- mDialogContentView.addView(row.view, mDialogContentView.getChildCount() - 2);
+ mDialogRowsView.addView(row.view);
mRows.add(row);
}
@@ -321,23 +318,10 @@
for (int i = 0; i < N; i++) {
final VolumeRow row = mRows.get(i);
initRow(row, row.stream, row.iconRes, row.iconMuteRes, row.important);
- if (i > 0) {
- addSpacer(row);
- }
- mDialogContentView.addView(row.view, mDialogContentView.getChildCount() - 2);
+ mDialogRowsView.addView(row.view);
}
}
- private void addSpacer(VolumeRow row) {
- final View v = new View(mContext);
- v.setId(android.R.id.background);
- final int h = mContext.getResources()
- .getDimensionPixelSize(R.dimen.volume_slider_interspacing);
- final LinearLayout.LayoutParams lp =
- new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, h);
- mDialogContentView.addView(v, mDialogContentView.getChildCount() - 2, lp);
- row.space = v;
- }
private boolean isAttached() {
return mDialogContentView != null && mDialogContentView.isAttachedToWindow();
@@ -391,12 +375,15 @@
row.iconMuteRes = iconMuteRes;
row.important = important;
row.view = mDialog.getLayoutInflater().inflate(R.layout.volume_dialog_row, null);
+ row.view.setId(row.stream);
row.view.setTag(row);
row.header = (TextView) row.view.findViewById(R.id.volume_row_header);
+ row.header.setId(20 * row.stream);
mSpTexts.add(row.header);
row.slider = (SeekBar) row.view.findViewById(R.id.volume_row_slider);
row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row));
row.anim = null;
+ row.cachedShowHeaders = VolumePrefs.DEFAULT_SHOW_HEADERS;
// forward events above the slider into the slider
row.view.setOnTouchListener(new OnTouchListener() {
@@ -508,7 +495,7 @@
mMotion.startDismiss(new Runnable() {
@Override
public void run() {
- setExpandedH(false);
+ updateExpandedH(false /* expanding */, true /* dismissing */);
}
});
if (mAccessibilityMgr.isEnabled()) {
@@ -554,15 +541,67 @@
mHandler.sendEmptyMessageDelayed(H.UPDATE_BOTTOM_MARGIN, getConservativeCollapseDuration());
}
- private void setExpandedH(boolean expanded) {
+ private void updateExpandedH(final boolean expanded, final boolean dismissing) {
if (mExpanded == expanded) return;
mExpanded = expanded;
mExpandButtonAnimationRunning = isAttached();
- if (D.BUG) Log.d(TAG, "setExpandedH " + expanded);
- if (!mExpanded && mExpandButtonAnimationRunning) {
- prepareForCollapse();
+ if (D.BUG) Log.d(TAG, "updateExpandedH " + expanded);
+ updateExpandButtonH();
+ updateFooterH();
+ TransitionManager.endTransitions(mDialogView);
+ final VolumeRow activeRow = getActiveRow();
+ if (!dismissing) {
+ mWindow.setLayout(mWindow.getAttributes().width, ViewGroup.LayoutParams.MATCH_PARENT);
+ AutoTransition transition = new AutoTransition();
+ transition.setDuration(mExpandButtonAnimationDuration);
+ transition.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ transition.addListener(new Transition.TransitionListener() {
+ @Override
+ public void onTransitionStart(Transition transition) {
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ mWindow.setLayout(
+ mWindow.getAttributes().width, ViewGroup.LayoutParams.WRAP_CONTENT);
+ }
+
+ @Override
+ public void onTransitionCancel(Transition transition) {
+ }
+
+ @Override
+ public void onTransitionPause(Transition transition) {
+ mWindow.setLayout(
+ mWindow.getAttributes().width, ViewGroup.LayoutParams.WRAP_CONTENT);
+ }
+
+ @Override
+ public void onTransitionResume(Transition transition) {
+ }
+ });
+ TransitionManager.beginDelayedTransition(mDialogView, transition);
}
- updateRowsH();
+ updateRowsH(activeRow);
+ rescheduleTimeoutH();
+ }
+
+ private void updateExpandButtonH() {
+ if (D.BUG) Log.d(TAG, "updateExpandButtonH");
+ mExpandButton.setClickable(!mExpandButtonAnimationRunning);
+ if (!(mExpandButtonAnimationRunning && isAttached())) {
+ final int res = mExpanded ? R.drawable.ic_volume_collapse_animation
+ : R.drawable.ic_volume_expand_animation;
+ if (hasTouchFeature()) {
+ mExpandButton.setImageResource(res);
+ } else {
+ // if there is no touch feature, show the volume ringer instead
+ mExpandButton.setImageResource(R.drawable.ic_volume_ringer);
+ mExpandButton.setBackgroundResource(0); // remove gray background emphasis
+ }
+ mExpandButton.setContentDescription(mContext.getString(mExpanded ?
+ R.string.accessibility_volume_collapse : R.string.accessibility_volume_expand));
+ }
if (mExpandButtonAnimationRunning) {
final Drawable d = mExpandButton.getDrawable();
if (d instanceof AnimatedVectorDrawable) {
@@ -581,61 +620,41 @@
}, mExpandButtonAnimationDuration);
}
}
- rescheduleTimeoutH();
}
- private void updateExpandButtonH() {
- if (D.BUG) Log.d(TAG, "updateExpandButtonH");
- mExpandButton.setClickable(!mExpandButtonAnimationRunning);
- if (mExpandButtonAnimationRunning && isAttached()) return;
- final int res = mExpanded ? R.drawable.ic_volume_collapse_animation
- : R.drawable.ic_volume_expand_animation;
- if (hasTouchFeature()) {
- mExpandButton.setImageResource(res);
- } else {
- // if there is no touch feature, show the volume ringer instead
- mExpandButton.setImageResource(R.drawable.ic_volume_ringer);
- mExpandButton.setBackgroundResource(0); // remove gray background emphasis
- }
- mExpandButton.setContentDescription(mContext.getString(mExpanded ?
- R.string.accessibility_volume_collapse : R.string.accessibility_volume_expand));
- }
-
- private boolean isVisibleH(VolumeRow row, boolean isActive) {
+ private boolean shouldBeVisibleH(VolumeRow row, boolean isActive) {
return mExpanded && row.view.getVisibility() == View.VISIBLE
|| (mExpanded && (row.important || isActive))
|| !mExpanded && isActive;
}
- private void updateRowsH() {
+ private void updateRowsH(final VolumeRow activeRow) {
if (D.BUG) Log.d(TAG, "updateRowsH");
- final VolumeRow activeRow = getActiveRow();
- updateFooterH();
- updateExpandButtonH();
if (!mShowing) {
trimObsoleteH();
}
+ Util.setVisOrGone(mDialogRowsView.findViewById(R.id.spacer), mExpanded);
// apply changes to all rows
- for (VolumeRow row : mRows) {
+ for (final VolumeRow row : mRows) {
final boolean isActive = row == activeRow;
- final boolean visible = isVisibleH(row, isActive);
- Util.setVisOrGone(row.view, visible);
- Util.setVisOrGone(row.space, visible && mExpanded);
- updateVolumeRowHeaderVisibleH(row);
- row.header.setAlpha(mExpanded && isActive ? 1 : 0.5f);
- updateVolumeRowSliderTintH(row, isActive);
+ final boolean shouldBeVisible = shouldBeVisibleH(row, isActive);
+ Util.setVisOrGone(row.view, shouldBeVisible);
+ if (row.view.isShown()) {
+ updateVolumeRowHeaderVisibleH(row);
+ updateVolumeRowSliderTintH(row, isActive);
+ }
}
+
}
private void trimObsoleteH() {
if (D.BUG) Log.d(TAG, "trimObsoleteH");
- for (int i = mRows.size() -1; i >= 0; i--) {
+ for (int i = mRows.size() - 1; i >= 0; i--) {
final VolumeRow row = mRows.get(i);
if (row.ss == null || !row.ss.dynamic) continue;
if (!mDynamic.get(row.stream)) {
mRows.remove(i);
- mDialogContentView.removeView(row.view);
- mDialogContentView.removeView(row.space);
+ mDialogRowsView.removeView(row.view);
}
}
}
@@ -662,7 +681,7 @@
if (mActiveStream != state.activeStream) {
mActiveStream = state.activeStream;
- updateRowsH();
+ updateRowsH(getActiveRow());
rescheduleTimeoutH();
}
for (VolumeRow row : mRows) {
@@ -723,9 +742,6 @@
&& mState.ringerModeInternal == AudioManager.RINGER_MODE_SILENT;
final boolean isZenAlarms = mState.zenMode == Global.ZEN_MODE_ALARMS;
final boolean isZenNone = mState.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
- final boolean isZenPriority = mState.zenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- final boolean isRingZenNone = (isRingStream || isSystemStream) && isZenNone;
- final boolean isRingLimited = isRingStream && isZenPriority;
final boolean zenMuted = isZenAlarms ? (isRingStream || isSystemStream)
: isZenNone ? (isRingStream || isSystemStream || isAlarmStream || isMusicStream)
: false;
@@ -740,21 +756,7 @@
updateVolumeRowHeaderVisibleH(row);
// update header text
- String text = ss.name;
- if (mShowHeaders) {
- if (isRingZenNone) {
- text = mContext.getString(R.string.volume_stream_muted_dnd, ss.name);
- } else if (isRingVibrate && isRingLimited) {
- text = mContext.getString(R.string.volume_stream_vibrate_dnd, ss.name);
- } else if (isRingVibrate) {
- text = mContext.getString(R.string.volume_stream_vibrate, ss.name);
- } else if (ss.muted || mAutomute && ss.level == 0) {
- text = mContext.getString(R.string.volume_stream_muted, ss.name);
- } else if (isRingLimited) {
- text = mContext.getString(R.string.volume_stream_limited_dnd, ss.name);
- }
- }
- Util.setText(row.header, text);
+ Util.setText(row.header, ss.name);
// update icon
final boolean iconEnabled = (mAutomute || ss.muteSupported) && !zenMuted;
@@ -823,7 +825,7 @@
private void updateVolumeRowHeaderVisibleH(VolumeRow row) {
final boolean dynamic = row.ss != null && row.ss.dynamic;
- final boolean showHeaders = mShowHeaders || mExpanded && dynamic;
+ final boolean showHeaders = mExpanded && (mShowHeaders || dynamic);
if (row.cachedShowHeaders != showHeaders) {
row.cachedShowHeaders = showHeaders;
Util.setVisOrGone(row.header, showHeaders);
@@ -979,6 +981,7 @@
mDialog.dismiss();
mZenFooter.cleanup();
initDialog();
+ mDensity = density;
}
updateWindowWidthH();
mSpTexts.update();
@@ -1028,7 +1031,7 @@
if (mExpandButtonAnimationRunning) return;
final boolean newExpand = !mExpanded;
Events.writeEvent(mContext, Events.EVENT_EXPAND, newExpand);
- setExpandedH(newExpand);
+ updateExpandedH(newExpand, false /* dismissing */);
}
};
@@ -1225,7 +1228,6 @@
private static class VolumeRow {
private View view;
- private View space;
private TextView header;
private ImageButton icon;
private SeekBar slider;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index 3d33809..44a435e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -122,7 +122,6 @@
private void applyConfiguration() {
mDialog.setStreamImportant(AudioManager.STREAM_ALARM, true);
mDialog.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
- mDialog.setShowHeaders(false);
mDialog.setAutomute(true);
mDialog.setSilentMode(false);
mController.setVolumePolicy(mVolumePolicy);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java
index bbb70ed..04339eb 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java
@@ -43,7 +43,7 @@
public static final String PREF_ADJUST_ALARMS = "pref_adjust_alarms";
public static final String PREF_ADJUST_NOTIFICATION = "pref_adjust_notification";
- public static final boolean DEFAULT_SHOW_HEADERS = false;
+ public static final boolean DEFAULT_SHOW_HEADERS = true;
public static final boolean DEFAULT_ENABLE_AUTOMUTE = true;
public static final boolean DEFAULT_ENABLE_SILENT_MODE = true;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
index f01e95f..995ecae 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
@@ -124,12 +124,8 @@
: null;
Util.setText(mSummaryLine1, line1);
- final boolean isForever = mConfig != null && mConfig.manualRule != null
- && mConfig.manualRule.conditionId == null;
- final CharSequence line2 =
- isForever ? mContext.getString(com.android.internal.R.string.zen_mode_forever_dnd)
- : ZenModeConfig.getConditionSummary(mContext, mConfig, mController.getCurrentUser(),
- true /*shortVersion*/);
+ final CharSequence line2 = ZenModeConfig.getConditionSummary(mContext, mConfig,
+ mController.getCurrentUser(), true /*shortVersion*/);
Util.setText(mSummaryLine2, line2);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/phone/DozeParametersTests.java b/packages/SystemUI/tests/src/com/android/systemui/phone/DozeParametersTests.java
new file mode 100644
index 0000000..07334f3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/phone/DozeParametersTests.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.phone;
+
+import com.android.systemui.statusbar.phone.DozeParameters.IntInOutMatcher;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+@SmallTest
+public class DozeParametersTests extends AndroidTestCase {
+
+ public void test_inOutMatcher_defaultIn() {
+ IntInOutMatcher intInOutMatcher = new IntInOutMatcher("*");
+
+ assertTrue(intInOutMatcher.isIn(1));
+ assertTrue(intInOutMatcher.isIn(-1));
+ assertTrue(intInOutMatcher.isIn(0));
+ }
+
+ public void test_inOutMatcher_defaultOut() {
+ IntInOutMatcher intInOutMatcher = new IntInOutMatcher("!*");
+
+ assertFalse(intInOutMatcher.isIn(1));
+ assertFalse(intInOutMatcher.isIn(-1));
+ assertFalse(intInOutMatcher.isIn(0));
+ }
+
+ public void test_inOutMatcher_someIn() {
+ IntInOutMatcher intInOutMatcher = new IntInOutMatcher("1,2,3,!*");
+
+ assertTrue(intInOutMatcher.isIn(1));
+ assertTrue(intInOutMatcher.isIn(2));
+ assertTrue(intInOutMatcher.isIn(3));
+
+ assertFalse(intInOutMatcher.isIn(0));
+ assertFalse(intInOutMatcher.isIn(4));
+ }
+
+ public void test_inOutMatcher_someOut() {
+ IntInOutMatcher intInOutMatcher = new IntInOutMatcher("!1,!2,!3,*");
+
+ assertFalse(intInOutMatcher.isIn(1));
+ assertFalse(intInOutMatcher.isIn(2));
+ assertFalse(intInOutMatcher.isIn(3));
+
+ assertTrue(intInOutMatcher.isIn(0));
+ assertTrue(intInOutMatcher.isIn(4));
+ }
+
+ public void test_inOutMatcher_mixed() {
+ IntInOutMatcher intInOutMatcher = new IntInOutMatcher("!1,2,!3,*");
+
+ assertFalse(intInOutMatcher.isIn(1));
+ assertTrue(intInOutMatcher.isIn(2));
+ assertFalse(intInOutMatcher.isIn(3));
+
+ assertTrue(intInOutMatcher.isIn(0));
+ assertTrue(intInOutMatcher.isIn(4));
+ }
+
+ public void test_inOutMatcher_failEmpty() {
+ try {
+ new IntInOutMatcher("");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void test_inOutMatcher_failNull() {
+ try {
+ new IntInOutMatcher(null);
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void test_inOutMatcher_failEmptyClause() {
+ try {
+ new IntInOutMatcher("!1,*,");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void test_inOutMatcher_failDuplicate() {
+ try {
+ new IntInOutMatcher("!1,*,!1");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void test_inOutMatcher_failDuplicateDefault() {
+ try {
+ new IntInOutMatcher("!1,*,*");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void test_inOutMatcher_failMalformedNot() {
+ try {
+ new IntInOutMatcher("!,*");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void test_inOutMatcher_failText() {
+ try {
+ new IntInOutMatcher("!abc,*");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void test_inOutMatcher_failContradiction() {
+ try {
+ new IntInOutMatcher("1,!1,*");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void test_inOutMatcher_failContradictionDefault() {
+ try {
+ new IntInOutMatcher("1,*,!*");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void test_inOutMatcher_failMissingDefault() {
+ try {
+ new IntInOutMatcher("1");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java
index c93377a..7703c58 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java
@@ -58,7 +58,7 @@
mHandler = new Handler(mThread.getLooper());
ComponentName component = new ComponentName(mContext, FakeTileService.class);
mStateManager = new TileLifecycleManager(mHandler, getContext(),
- Mockito.mock(IQSService.class), new Tile(component),
+ Mockito.mock(IQSService.class), new Tile(),
new Intent().setComponent(component),
new UserHandle(UserHandle.myUserId()));
mCallbacks.clear();
diff --git a/packages/WallpaperBackup/AndroidManifest.xml b/packages/WallpaperBackup/AndroidManifest.xml
index b8cea20..c548101 100644
--- a/packages/WallpaperBackup/AndroidManifest.xml
+++ b/packages/WallpaperBackup/AndroidManifest.xml
@@ -24,6 +24,7 @@
android:process="system"
android:killAfterRestore="false"
android:allowBackup="true"
+ android:backupInForeground="true"
android:backupAgent=".WallpaperBackupAgent"
android:fullBackupOnly="true" >
</application>
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
index 47d1493..3a82f88 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -16,20 +16,31 @@
package com.android.wallpaperbackup;
+import static android.app.WallpaperManager.FLAG_LOCK;
+import static android.app.WallpaperManager.FLAG_SYSTEM;
+
+import android.app.AppGlobals;
import android.app.WallpaperManager;
import android.app.backup.BackupAgent;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.FullBackupDataOutput;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
import android.graphics.Rect;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import android.os.UserHandle;
-import android.system.Os;
import android.util.Slog;
import android.util.Xml;
+import libcore.io.IoUtils;
+
import org.xmlpull.v1.XmlPullParser;
import java.io.File;
@@ -47,15 +58,28 @@
// Target filenames within the system's wallpaper directory
static final String WALLPAPER = "wallpaper_orig";
+ static final String WALLPAPER_LOCK = "wallpaper_lock_orig";
static final String WALLPAPER_INFO = "wallpaper_info.xml";
// Names of our local-data stage files/links
static final String IMAGE_STAGE = "wallpaper-stage";
+ static final String LOCK_IMAGE_STAGE = "wallpaper-lock-stage";
static final String INFO_STAGE = "wallpaper-info-stage";
static final String EMPTY_SENTINEL = "empty";
+ static final String QUOTA_SENTINEL = "quota";
- private File mWallpaperInfo; // wallpaper metadata file
- private File mWallpaperFile; // primary wallpaper image file
+ // Not-for-backup bookkeeping
+ static final String PREFS_NAME = "wbprefs.xml";
+ static final String SYSTEM_GENERATION = "system_gen";
+ static final String LOCK_GENERATION = "lock_gen";
+
+ private File mWallpaperInfo; // wallpaper metadata file
+ private File mWallpaperFile; // primary wallpaper image file
+ private File mLockWallpaperFile; // lock wallpaper image file
+
+ // If this file exists, it means we exceeded our quota last time
+ private File mQuotaFile;
+ private boolean mQuotaExceeded;
private WallpaperManager mWm;
@@ -68,7 +92,14 @@
File wallpaperDir = Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM);
mWallpaperInfo = new File(wallpaperDir, WALLPAPER_INFO);
mWallpaperFile = new File(wallpaperDir, WALLPAPER);
+ mLockWallpaperFile = new File(wallpaperDir, WALLPAPER_LOCK);
mWm = (WallpaperManager) getSystemService(Context.WALLPAPER_SERVICE);
+
+ mQuotaFile = new File(getFilesDir(), QUOTA_SENTINEL);
+ mQuotaExceeded = mQuotaFile.exists();
+ if (DEBUG) {
+ Slog.v(TAG, "quota file " + mQuotaFile.getPath() + " exists=" + mQuotaExceeded);
+ }
}
@Override
@@ -77,6 +108,7 @@
final File filesDir = getFilesDir();
final File infoStage = new File(filesDir, INFO_STAGE);
final File imageStage = new File (filesDir, IMAGE_STAGE);
+ final File lockImageStage = new File (filesDir, LOCK_IMAGE_STAGE);
final File empty = new File (filesDir, EMPTY_SENTINEL);
try {
@@ -87,33 +119,80 @@
touch.close();
fullBackupFile(empty, data);
- // only back up the wallpaper if we've been told it's allowed
- if (mWm.isWallpaperBackupEligible()) {
- if (DEBUG) {
- Slog.v(TAG, "Wallpaper is backup-eligible; linking & writing");
+ SharedPreferences prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
+ final int lastSysGeneration = prefs.getInt(SYSTEM_GENERATION, -1);
+ final int lastLockGeneration = prefs.getInt(LOCK_GENERATION, -1);
+
+ final int sysGeneration =
+ mWm.getWallpaperIdForUser(FLAG_SYSTEM, UserHandle.USER_SYSTEM);
+ final int lockGeneration =
+ mWm.getWallpaperIdForUser(FLAG_LOCK, UserHandle.USER_SYSTEM);
+ final boolean sysChanged = (sysGeneration != lastSysGeneration);
+ final boolean lockChanged = (lockGeneration != lastLockGeneration);
+
+ final boolean sysEligible = mWm.isWallpaperBackupEligible(FLAG_SYSTEM);
+ final boolean lockEligible = mWm.isWallpaperBackupEligible(FLAG_LOCK);
+
+ // There might be a latent lock wallpaper file present but unused: don't
+ // include it in the backup if that's the case.
+ ParcelFileDescriptor lockFd = mWm.getWallpaperFile(FLAG_LOCK, UserHandle.USER_SYSTEM);
+ final boolean hasLockWallpaper = (lockFd != null);
+ IoUtils.closeQuietly(lockFd);
+
+ if (DEBUG) {
+ Slog.v(TAG, "sysGen=" + sysGeneration + " : sysChanged=" + sysChanged);
+ Slog.v(TAG, "lockGen=" + lockGeneration + " : lockChanged=" + lockChanged);
+ Slog.v(TAG, "sysEligble=" + sysEligible);
+ Slog.v(TAG, "lockEligible=" + lockEligible);
+ }
+
+ // only back up the wallpapers if we've been told they're eligible
+ if ((sysEligible || lockEligible) && mWallpaperInfo.exists()) {
+ if (sysChanged || lockChanged || !infoStage.exists()) {
+ if (DEBUG) Slog.v(TAG, "New wallpaper configuration; copying");
+ FileUtils.copyFileOrThrow(mWallpaperInfo, infoStage);
}
-
- // In case of prior muddled state
- infoStage.delete();
- imageStage.delete();
-
- Os.link(mWallpaperInfo.getCanonicalPath(), infoStage.getCanonicalPath());
fullBackupFile(infoStage, data);
- Os.link(mWallpaperFile.getCanonicalPath(), imageStage.getCanonicalPath());
- fullBackupFile(imageStage, data);
- } else {
- if (DEBUG) {
- Slog.v(TAG, "Wallpaper not backup-eligible; writing no data");
+ }
+ if (sysEligible && mWallpaperFile.exists()) {
+ if (sysChanged || !imageStage.exists()) {
+ if (DEBUG) Slog.v(TAG, "New system wallpaper; copying");
+ FileUtils.copyFileOrThrow(mWallpaperFile, imageStage);
}
+ fullBackupFile(imageStage, data);
+ prefs.edit().putInt(SYSTEM_GENERATION, sysGeneration).apply();
+ }
+
+ // Don't try to store the lock image if we overran our quota last time
+ if (lockEligible && hasLockWallpaper && mLockWallpaperFile.exists() && !mQuotaExceeded) {
+ if (lockChanged || !lockImageStage.exists()) {
+ if (DEBUG) Slog.v(TAG, "New lock wallpaper; copying");
+ FileUtils.copyFileOrThrow(mLockWallpaperFile, lockImageStage);
+ }
+ fullBackupFile(lockImageStage, data);
+ prefs.edit().putInt(LOCK_GENERATION, lockGeneration).apply();
}
} catch (Exception e) {
- Slog.e(TAG, "Unable to back up wallpaper: " + e.getMessage());
+ Slog.e(TAG, "Unable to back up wallpaper", e);
} finally {
- if (DEBUG) {
- Slog.v(TAG, "Removing backup stage links");
- }
- infoStage.delete();
- imageStage.delete();
+ // Even if this time we had to back off on attempting to store the lock image
+ // due to exceeding the data quota, try again next time. This will alternate
+ // between "try both" and "only store the primary image" until either there
+ // is no lock image to store, or the quota is raised, or both fit under the
+ // quota.
+ mQuotaFile.delete();
+ }
+ }
+
+ @Override
+ public void onQuotaExceeded(long backupDataBytes, long quotaBytes) {
+ if (DEBUG) {
+ Slog.i(TAG, "Quota exceeded (" + backupDataBytes + " vs " + quotaBytes + ')');
+ }
+ try (FileOutputStream f = new FileOutputStream(mQuotaFile)) {
+ f.write(0);
+ } catch (Exception e) {
+ Slog.w(TAG, "Unable to record quota-exceeded: " + e.getMessage());
}
}
@@ -124,42 +203,75 @@
if (DEBUG) {
Slog.v(TAG, "onRestoreFinished()");
}
- final File infoStage = new File(getFilesDir(), INFO_STAGE);
- final File imageStage = new File (getFilesDir(), IMAGE_STAGE);
+ final File filesDir = getFilesDir();
+ final File infoStage = new File(filesDir, INFO_STAGE);
+ final File imageStage = new File (filesDir, IMAGE_STAGE);
+ final File lockImageStage = new File (filesDir, LOCK_IMAGE_STAGE);
+
+ // If we restored separate lock imagery, the system wallpaper should be
+ // applied as system-only; but if there's no separate lock image, make
+ // sure to apply the restored system wallpaper as both.
+ final int sysWhich = FLAG_SYSTEM | (lockImageStage.exists() ? 0 : FLAG_LOCK);
try {
- // It is valid for the imagery to be absent; it means that we were not permitted
- // to back up the original image on the source device.
- if (imageStage.exists()) {
- if (DEBUG) {
- Slog.v(TAG, "Got restored wallpaper; applying");
- }
+ // First off, revert to the factory state
+ mWm.clear(FLAG_SYSTEM | FLAG_LOCK);
- // Parse the restored info file to find the crop hint. Note that this currently
- // relies on a priori knowledge of the wallpaper info file schema.
- Rect cropHint = parseCropHint(infoStage);
- if (cropHint != null) {
- if (DEBUG) {
- Slog.v(TAG, "Restored crop hint " + cropHint + "; now writing data");
- }
- WallpaperManager wm = getSystemService(WallpaperManager.class);
- try (FileInputStream in = new FileInputStream(imageStage)) {
- wm.setStream(in, cropHint, true, WallpaperManager.FLAG_SYSTEM);
- } finally {} // auto-closes 'in'
+ // It is valid for the imagery to be absent; it means that we were not permitted
+ // to back up the original image on the source device, or there was no user-supplied
+ // wallpaper image present.
+ restoreFromStage(imageStage, infoStage, "wp", sysWhich);
+ restoreFromStage(lockImageStage, infoStage, "kwp", FLAG_LOCK);
+
+ // And reset to the wallpaper service we should be using
+ ComponentName wpService = parseWallpaperComponent(infoStage, "wp");
+ if (servicePackageExists(wpService)) {
+ if (DEBUG) {
+ Slog.i(TAG, "Using wallpaper service " + wpService);
+ }
+ mWm.setWallpaperComponent(wpService, UserHandle.USER_SYSTEM);
+ } else {
+ if (DEBUG) {
+ Slog.v(TAG, "Can't use wallpaper service " + wpService);
}
}
} catch (Exception e) {
Slog.e(TAG, "Unable to restore wallpaper: " + e.getMessage());
} finally {
if (DEBUG) {
- Slog.v(TAG, "Removing restore stage files");
+ Slog.v(TAG, "Restore finished; clearing backup bookkeeping");
}
infoStage.delete();
imageStage.delete();
+ lockImageStage.delete();
+
+ SharedPreferences prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
+ prefs.edit()
+ .putInt(SYSTEM_GENERATION, -1)
+ .putInt(LOCK_GENERATION, -1)
+ .commit();
}
}
- private Rect parseCropHint(File wallpaperInfo) {
+ private void restoreFromStage(File stage, File info, String hintTag, int which)
+ throws IOException {
+ if (stage.exists()) {
+ // Parse the restored info file to find the crop hint. Note that this currently
+ // relies on a priori knowledge of the wallpaper info file schema.
+ Rect cropHint = parseCropHint(info, hintTag);
+ if (cropHint != null) {
+ Slog.i(TAG, "Got restored wallpaper; applying which=" + which);
+ if (DEBUG) {
+ Slog.v(TAG, "Restored crop hint " + cropHint);
+ }
+ try (FileInputStream in = new FileInputStream(stage)) {
+ mWm.setStream(in, cropHint.isEmpty() ? null : cropHint, true, which);
+ } finally {} // auto-closes 'in'
+ }
+ }
+ }
+
+ private Rect parseCropHint(File wallpaperInfo, String sectionTag) {
Rect cropHint = new Rect();
try (FileInputStream stream = new FileInputStream(wallpaperInfo)) {
XmlPullParser parser = Xml.newPullParser();
@@ -170,7 +282,7 @@
type = parser.next();
if (type == XmlPullParser.START_TAG) {
String tag = parser.getName();
- if ("wp".equals(tag)) {
+ if (sectionTag.equals(tag)) {
cropHint.left = getAttributeInt(parser, "cropLeft", 0);
cropHint.top = getAttributeInt(parser, "cropTop", 0);
cropHint.right = getAttributeInt(parser, "cropRight", 0);
@@ -180,18 +292,60 @@
} while (type != XmlPullParser.END_DOCUMENT);
} catch (Exception e) {
// Whoops; can't process the info file at all. Report failure.
- Slog.w(TAG, "Failed to parse restored metadata: " + e.getMessage());
+ Slog.w(TAG, "Failed to parse restored crop: " + e.getMessage());
return null;
}
return cropHint;
}
+ private ComponentName parseWallpaperComponent(File wallpaperInfo, String sectionTag) {
+ ComponentName name = null;
+ try (FileInputStream stream = new FileInputStream(wallpaperInfo)) {
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, StandardCharsets.UTF_8.name());
+
+ int type;
+ do {
+ type = parser.next();
+ if (type == XmlPullParser.START_TAG) {
+ String tag = parser.getName();
+ if (sectionTag.equals(tag)) {
+ final String parsedName = parser.getAttributeValue(null, "component");
+ name = (parsedName != null)
+ ? ComponentName.unflattenFromString(parsedName)
+ : null;
+ break;
+ }
+ }
+ } while (type != XmlPullParser.END_DOCUMENT);
+ } catch (Exception e) {
+ // Whoops; can't process the info file at all. Report failure.
+ Slog.w(TAG, "Failed to parse restored component: " + e.getMessage());
+ return null;
+ }
+ return name;
+ }
+
private int getAttributeInt(XmlPullParser parser, String name, int defValue) {
final String value = parser.getAttributeValue(null, name);
return (value == null) ? defValue : Integer.parseInt(value);
}
+ private boolean servicePackageExists(ComponentName comp) {
+ try {
+ if (comp != null) {
+ final IPackageManager pm = AppGlobals.getPackageManager();
+ final PackageInfo info = pm.getPackageInfo(comp.getPackageName(),
+ 0, UserHandle.USER_SYSTEM);
+ return (info != null);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to contact package manager");
+ }
+ return false;
+ }
+
//
// Key/value API: abstract, therefore required; but not used
//
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index a1487e3..5099db7 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -33,73 +33,61 @@
// OPEN: Settings > Accessibility
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACCESSIBILITY = 2;
// OPEN: Settings > Accessibility > Captions
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACCESSIBILITY_CAPTION_PROPERTIES = 3;
// OPEN: Settings > Accessibility > [Service]
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACCESSIBILITY_SERVICE = 4;
// OPEN: Settings > Accessibility > Color correction
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACCESSIBILITY_TOGGLE_DALTONIZER = 5;
// OPEN: Settings > Accessibility > Accessibility shortcut
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACCESSIBILITY_TOGGLE_GLOBAL_GESTURE = 6;
// OPEN: Settings > Accessibility > Magnification gestures
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFICATION = 7;
// OPEN: Settings > Accounts
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACCOUNT = 8;
// OPEN: Settings > Accounts > [Single Account Sync Settings]
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACCOUNTS_ACCOUNT_SYNC = 9;
// OPEN: Settings > Accounts > Add an account
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACCOUNTS_CHOOSE_ACCOUNT_ACTIVITY = 10;
// OPEN: Settings > Accounts > [List of accounts when more than one]
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACCOUNTS_MANAGE_ACCOUNTS = 11;
// OPEN: Settings > Cellular network settings > APNs
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
APN = 12;
// OPEN: Settings > More > Cellular network settings > APNs > [Edit APN]
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
APN_EDITOR = 13;
// OBSOLETE
@@ -114,7 +102,6 @@
// OPEN: Settings > Apps > Configure apps > App links > [App]
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
APPLICATIONS_APP_LAUNCH = 17;
// OBSOLETE
@@ -123,19 +110,16 @@
// OPEN: Settings > Internal storage > Apps storage > [App]
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
APPLICATIONS_APP_STORAGE = 19;
// OPEN: Settings > Apps > [App info]
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
APPLICATIONS_INSTALLED_APP_DETAILS = 20;
// OPEN: Settings > Memory > App usage > [App Memory usage]
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
APPLICATIONS_PROCESS_STATS_DETAIL = 21;
// OBSOLETE
@@ -144,19 +128,16 @@
// OPEN: Settings > Memory > App usage
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
APPLICATIONS_PROCESS_STATS_UI = 23;
// OPEN: Settings > Bluetooth
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
BLUETOOTH = 24;
// OPEN: Choose Bluetooth device (ex: when sharing)
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
BLUETOOTH_DEVICE_PICKER = 25;
// OBSOLETE
@@ -165,55 +146,46 @@
// OPEN: Settings > Security > Choose screen lock
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
CHOOSE_LOCK_GENERIC = 27;
// OPEN: Settings > Security > Choose screen lock > Choose your password
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
CHOOSE_LOCK_PASSWORD = 28;
// OPEN: Settings > Security > Choose screen lock > Choose your pattern
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
CHOOSE_LOCK_PATTERN = 29;
// OPEN: Settings > Security > Choose screen lock > Confirm your password
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
CONFIRM_LOCK_PASSWORD = 30;
// OPEN: Settings > Security > Choose screen lock > Confirm your pattern
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
CONFIRM_LOCK_PATTERN = 31;
// OPEN: Settings > Security > Encrypt phone
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
CRYPT_KEEPER = 32;
// OPEN: Settings > Security > Encrypt phone > Confirm
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
CRYPT_KEEPER_CONFIRM = 33;
// OPEN: Settings > Search results
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
DASHBOARD_SEARCH_RESULTS = 34;
// OPEN: Settings (Root page)
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
DASHBOARD_SUMMARY = 35;
// OBSOLETE
@@ -222,49 +194,41 @@
// OPEN: Settings > Data usage
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
DATA_USAGE_SUMMARY = 37;
// OPEN: Settings > Date & time
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
DATE_TIME = 38;
// OPEN: Settings > Developer options
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
DEVELOPMENT = 39;
// OPEN: Settings > About phone
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
DEVICEINFO = 40;
// OPEN: Settings > About phone > Status > IMEI information
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
DEVICEINFO_IMEI_INFORMATION = 41;
// OPEN: Settings > Internal storage
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
DEVICEINFO_STORAGE = 42;
// OPEN: Settings > About phone > Status > SIM status
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
DEVICEINFO_SIM_STATUS = 43;
// OPEN: Settings > About phone > Status
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
DEVICEINFO_STATUS = 44;
// OBSOLETE
@@ -273,25 +237,21 @@
// OPEN: Settings > Display
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
DISPLAY = 46;
// OPEN: Settings > Display > Daydream
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
DREAM = 47;
// OPEN: Settings > Security > Screen lock > Secure start-up
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ENCRYPTION = 48;
// OPEN: Settings > Security > Nexus Imprint
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
FINGERPRINT = 49;
// OBSOLETE
@@ -300,55 +260,46 @@
// OPEN: Settings > Battery > History details
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
FUELGAUGE_BATTERY_HISTORY_DETAIL = 51;
// OPEN: Settings > Battery > Battery saver
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
FUELGAUGE_BATTERY_SAVER = 52;
// OPEN: Settings > Battery > [App Use details]
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
FUELGAUGE_POWER_USAGE_DETAIL = 53;
// OPEN: Settings > Battery
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
FUELGAUGE_POWER_USAGE_SUMMARY = 54;
// OPEN: Settings > Home
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
HOME = 55;
// OPEN: Settings > Security > SIM card lock settings
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ICC_LOCK = 56;
// OPEN: Settings > Language & input
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
INPUTMETHOD_LANGUAGE = 57;
// OPEN: Settings > Language & input > Physical keyboard
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
INPUTMETHOD_KEYBOARD = 58;
// OPEN: Settings > Language & input > Spell checker
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
INPUTMETHOD_SPELL_CHECKERS = 59;
// OBSOLETE
@@ -357,79 +308,66 @@
// OPEN: Settings > Language & input > Personal dictionary
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
INPUTMETHOD_USER_DICTIONARY = 61;
// OPEN: Settings > Language & input > Add word
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
INPUTMETHOD_USER_DICTIONARY_ADD_WORD = 62;
// OPEN: Settings > Location
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
LOCATION = 63;
// OPEN: Settings > Location > Location mode
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
LOCATION_MODE = 64;
// OPEN: Settings > Apps
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
MANAGE_APPLICATIONS = 65;
// OPEN: Settings > Backup & reset > Factory data reset
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
MASTER_CLEAR = 66;
// OPEN: Settings > Backup & reset > Factory data reset > Confirm
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
MASTER_CLEAR_CONFIRM = 67;
// OPEN: Settings > Data usage > Network restrictions
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
NET_DATA_USAGE_METERED = 68;
// OPEN: Settings > More > Android Beam
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
NFC_BEAM = 69;
// OPEN: Settings > Tap & pay
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
NFC_PAYMENT = 70;
// OPEN: Settings > Sound & notification
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
NOTIFICATION = 71;
// OPEN: Settings > Sound & notification > App notifications > [App]
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
NOTIFICATION_APP_NOTIFICATION = 72;
// OPEN: Settings > Sound & notification > Other sounds
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
NOTIFICATION_OTHER_SOUND = 73;
// OBSOLETE
@@ -438,13 +376,11 @@
// OPEN: Settings Widget > Notification log
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
NOTIFICATION_STATION = 75;
// OPEN: Settings > Sound & notification > Do not disturb
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
NOTIFICATION_ZEN_MODE = 76;
// OPEN: OBSOLETE
@@ -453,25 +389,21 @@
// OPEN: Print job notification > Print job settings
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
PRINT_JOB_SETTINGS = 78;
// OPEN: Settings > Printing > [Print Service]
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
PRINT_SERVICE_SETTINGS = 79;
// OPEN: Settings > Printing
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
PRINT_SETTINGS = 80;
// OPEN: Settings > Backup & reset
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
PRIVACY = 81;
//OBSOLETE
@@ -480,37 +412,31 @@
// OPEN: Settings > Backup & reset > Network settings reset
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
RESET_NETWORK = 83;
// OPEN: Settings > Backup & reset > Network settings reset > Confirm
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
RESET_NETWORK_CONFIRM = 84;
// OPEN: Settings > Developer Options > Running Services
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
RUNNING_SERVICE_DETAILS = 85;
// OPEN: Settings > Security > Screen pinning
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
SCREEN_PINNING = 86;
// OPEN: Settings > Security
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
SECURITY = 87;
// OPEN: Settings > SIM cards
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
SIM = 88;
// OBSOLETE
@@ -519,55 +445,46 @@
// OPEN: Settings > More > Tethering & portable hotspot
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
TETHER = 90;
// OPEN: Settings > Security > Trust agents
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
TRUST_AGENT = 91;
// OPEN: Settings > Security > Trusted credentials
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
TRUSTED_CREDENTIALS = 92;
// OPEN: Settings > Language & input > TTS output > [Engine] > Settings
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
TTS_ENGINE_SETTINGS = 93;
// OPEN: Settings > Language & input > Text-to-speech output
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
TTS_TEXT_TO_SPEECH = 94;
// OPEN: Settings > Security > Apps with usage access
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
USAGE_ACCESS = 95;
// OPEN: Settings > Users
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
USER = 96;
// OPEN: Settings > Users > [Restricted profile app & content access]
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
USERS_APP_RESTRICTIONS = 97;
// OPEN: Settings > Users > [User settings]
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
USER_DETAILS = 98;
// OBSOLETE
@@ -576,43 +493,36 @@
// OPEN: Settings > More > VPN
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
VPN = 100;
// OPEN: Settings > Display > Choose wallpaper from
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
WALLPAPER_TYPE = 101;
// OPEN: Settings > Display > Cast
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
WFD_WIFI_DISPLAY = 102;
// OPEN: Settings > Wi-Fi
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
WIFI = 103;
// OPEN: Settings > Wi-Fi > Advanced Wi-Fi
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
WIFI_ADVANCED = 104;
// OPEN: Settings > More > Wi-Fi Calling
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
WIFI_CALLING = 105;
// OPEN: Settings > Wi-Fi > Saved networks
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
WIFI_SAVED_ACCESS_POINTS = 106;
// OBSOLETE
@@ -624,19 +534,16 @@
// OPEN: Settings > Wi-Fi > Advanced Wi-Fi > Wi-Fi Direct
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
WIFI_P2P = 109;
// OPEN: Settings > More
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
WIRELESS = 110;
// OPEN: Quick Settings Panel
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.26
QS_PANEL = 111;
// OPEN: QS Airplane mode tile shown
@@ -644,7 +551,6 @@
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.46
QS_AIRPLANEMODE = 112;
// OPEN: QS Bluetooth tile shown
@@ -652,21 +558,18 @@
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.46
QS_BLUETOOTH = 113;
// OPEN: QS Cast tile shown
// ACTION: QS Cast tile tapped
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.46
QS_CAST = 114;
// OPEN: QS Cellular tile shown
// ACTION: QS Cellular tile tapped
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.46
QS_CELLULAR = 115;
// OPEN: QS Color inversion tile shown
@@ -674,13 +577,11 @@
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.46
QS_COLORINVERSION = 116;
// OPEN: QS Cellular tile > Cellular detail panel
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.46
QS_DATAUSAGEDETAIL = 117;
// OPEN: QS Do not disturb tile shown
@@ -688,7 +589,6 @@
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.46
QS_DND = 118;
// OPEN: QS Flashlight tile shown
@@ -696,7 +596,6 @@
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.46
QS_FLASHLIGHT = 119;
// OPEN: QS Hotspot tile shown
@@ -704,14 +603,12 @@
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.46
QS_HOTSPOT = 120;
// OPEN: QS 3P tile shown
// ACTION: QS 3P tile tapped
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.46
QS_INTENT = 121;
// OPEN: QS Location tile shown
@@ -719,7 +616,6 @@
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.46
QS_LOCATION = 122;
// OPEN: QS Rotation tile shown
@@ -727,7 +623,6 @@
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.46
QS_ROTATIONLOCK = 123;
// OBSOLETE
@@ -736,7 +631,6 @@
// OPEN: QS User list panel
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.46
QS_USERDETAIL = 125;
// OPEN: QS WiFi tile shown
@@ -744,13 +638,11 @@
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.46
QS_WIFI = 126;
// OPEN: Notification Panel (including lockscreen)
// CATEGORY: NOTIFICATION
// OS: 5.1.1
- // GMS: 7.5.26
NOTIFICATION_PANEL = 127;
// OPEN: Notification in panel became visible.
@@ -764,7 +656,6 @@
// SUBTYPE: Dismiss reason from NotificationManagerService.java
// CATEGORY: NOTIFICATION
// OS: 5.1.1
- // GMS: 7.5.26
NOTIFICATION_ITEM = 128;
// ACTION: User tapped notification action
@@ -772,19 +663,16 @@
// SUBTYPE: Index of action on notification
// CATEGORY: NOTIFICATION
// OS: 5.0
- // GMS: 7.5.26
NOTIFICATION_ITEM_ACTION = 129;
// OPEN: Settings > Apps > Configure apps > App permissions
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
APPLICATIONS_ADVANCED = 130;
// OPEN: Settings > Location > Scanning
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
LOCATION_SCANNING = 131;
// OBSOLETE
@@ -793,43 +681,36 @@
// OPEN: Settings > Sound & notification > App notifications
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
MANAGE_APPLICATIONS_NOTIFICATIONS = 133;
// ACTION: Settings > Wi-Fi > Overflow > Add Network
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_WIFI_ADD_NETWORK = 134;
// ACTION: Settings > Wi-Fi > [Long press network] > Connect to network
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_WIFI_CONNECT = 135;
// ACTION: Settings > Wi-Fi > Overflow > Refresh
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_WIFI_FORCE_SCAN = 136;
// ACTION: Settings > Wi-Fi > [Long press network] > Forget network
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_WIFI_FORGET = 137;
// ACTION: Settings > Wi-Fi > Toggle off
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_WIFI_OFF = 138;
// ACTION: Settings > Wi-Fi > Toggle on
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_WIFI_ON = 139;
// OBSOLETE
@@ -838,280 +719,236 @@
// OPEN: Settings > Sound & notification > DND > Priority only allows
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
NOTIFICATION_ZEN_MODE_PRIORITY = 141;
// OPEN: Settings > Sound & notification > DND > Automatic rules
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
NOTIFICATION_ZEN_MODE_AUTOMATION = 142;
// OPEN: Settings > Apps > Configure apps > App links
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
MANAGE_DOMAIN_URLS = 143;
// OPEN: Settings > Sound & notification > DND > [Time based rule]
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
NOTIFICATION_ZEN_MODE_SCHEDULE_RULE = 144;
// OPEN: Settings > Sound & notification > DND > [External rule]
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
NOTIFICATION_ZEN_MODE_EXTERNAL_RULE = 145;
// OPEN: Settings > Sound & notification > DND > [Event rule]
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
NOTIFICATION_ZEN_MODE_EVENT_RULE = 146;
// ACTION: App notification settings > Block Notifications
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_BAN_APP_NOTES = 147;
// ACTION: Notification shade > Dismiss all button
// CATEGORY: NOTIFICATION
// OS: 6.0
- // GMS: 7.5.26
ACTION_DISMISS_ALL_NOTES = 148;
// OPEN: QS Do Not Disturb detail panel
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.26
QS_DND_DETAILS = 149;
// OPEN: QS Bluetooth detail panel
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.26
QS_BLUETOOTH_DETAILS = 150;
// OPEN: QS Cast detail panel
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.26
QS_CAST_DETAILS = 151;
// OPEN: QS Wi-Fi detail panel
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.26
QS_WIFI_DETAILS = 152;
// ACTION: QS Wi-Fi detail panel > Wi-Fi toggle
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.26
QS_WIFI_TOGGLE = 153;
// ACTION: QS Bluetooth detail panel > Bluetooth toggle
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.26
QS_BLUETOOTH_TOGGLE = 154;
// ACTION: QS Cellular detail panel > Cellular toggle
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.26
QS_CELLULAR_TOGGLE = 155;
// ACTION: QS User list panel > Select different user
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.26
QS_SWITCH_USER = 156;
// ACTION: QS Cast detail panel > Select cast device
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.26
QS_CAST_SELECT = 157;
// ACTION: QS Cast detail panel > Disconnect cast device
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.26
QS_CAST_DISCONNECT = 158;
// ACTION: Settings > Bluetooth > Toggle
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_BLUETOOTH_TOGGLE = 159;
// ACTION: Settings > Bluetooth > Overflow > Refresh
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_BLUETOOTH_SCAN = 160;
// ACTION: Settings > Bluetooth > Overflow > Rename this device
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_BLUETOOTH_RENAME = 161;
// ACTION: Settings > Bluetooth > Overflow > Show received files
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_BLUETOOTH_FILES = 162;
// ACTION: QS DND details panel > Increase / Decrease exit time
// SUBTYPE: true is increase, false is decrease
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.26
QS_DND_TIME = 163;
// ACTION: QS DND details panel > [Exit condition]
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.26
QS_DND_CONDITION_SELECT = 164;
// ACTION: QS DND details panel > [DND mode]
// SUBTYPE: 1 is priority, 2 is silence, 3 is alarms only
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.26
QS_DND_ZEN_SELECT = 165;
// ACTION: QS DND detail panel > DND toggle
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.26
QS_DND_TOGGLE = 166;
// ACTION: DND Settings > Priority only allows > Reminder toggle
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_ZEN_ALLOW_REMINDERS = 167;
// ACTION: DND Settings > Priority only allows > Event toggle
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_ZEN_ALLOW_EVENTS = 168;
// ACTION: DND Settings > Priority only allows > Messages
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_ZEN_ALLOW_MESSAGES = 169;
// ACTION: DND Settings > Priority only allows > Calls
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_ZEN_ALLOW_CALLS = 170;
// ACTION: DND Settings > Priority only allows > Repeat callers toggle
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_ZEN_ALLOW_REPEAT_CALLS = 171;
// ACTION: DND Settings > Automatic rules > Add rule
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_ZEN_ADD_RULE = 172;
// ACTION: DND Settings > Automatic rules > Add rule > OK
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_ZEN_ADD_RULE_OK = 173;
// ACTION: DND Settings > Automatic rules > [Rule] > Delete rule
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_ZEN_DELETE_RULE = 174;
// ACTION: DND Settings > Automatic rules > [Rule] > Delete rule > Delete
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_ZEN_DELETE_RULE_OK = 175;
// ACTION: DND Settings > Automatic rules > [Rule] > Toggle
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_ZEN_ENABLE_RULE = 176;
// ACTION: Settings > More > Airplane mode toggle
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_AIRPLANE_TOGGLE = 177;
// ACTION: Settings > Data usage > Cellular data toggle
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_CELL_DATA_TOGGLE = 178;
// OPEN: Settings > Sound & notification > Notification access
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
NOTIFICATION_ACCESS = 179;
// OPEN: Settings > Sound & notification > Do Not Disturb access
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
NOTIFICATION_ZEN_MODE_ACCESS = 180;
// OPEN: Settings > Apps > Configure apps > Default Apps
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
APPLICATIONS_DEFAULT_APPS = 181;
// OPEN: Settings > Internal storage > Apps storage
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
APPLICATIONS_STORAGE_APPS = 182;
// OPEN: Settings > Security > Usage access
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
APPLICATIONS_USAGE_ACCESS_DETAIL = 183;
// OPEN: Settings > Battery > Battery optimization
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
APPLICATIONS_HIGH_POWER_APPS = 184;
// OBSOLETE
@@ -1120,448 +957,377 @@
// ACTION: Lockscreen > Unlock gesture
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 5.1.1
- // GMS: 7.8.22
ACTION_LS_UNLOCK = 186;
// ACTION: Lockscreen > Pull shade open
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 5.1.1
- // GMS: 7.8.22
ACTION_LS_SHADE = 187;
// ACTION: Lockscreen > Tap on lock, shows hint
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 5.1.1
- // GMS: 7.8.22
ACTION_LS_HINT = 188;
// ACTION: Lockscreen > Camera
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 5.1.1
- // GMS: 7.8.22
ACTION_LS_CAMERA = 189;
// ACTION: Lockscreen > Dialer
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 5.1.1
- // GMS: 7.8.22
ACTION_LS_DIALER = 190;
// ACTION: Lockscreen > Tap on lock, locks phone
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 5.1.1
- // GMS: 7.8.22
ACTION_LS_LOCK = 191;
// ACTION: Lockscreen > Tap on notification, false touch rejection
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 5.1.1
- // GMS: 7.8.22
ACTION_LS_NOTE = 192;
// ACTION: Lockscreen > Swipe down to open quick settings
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 6.0
- // GMS: 7.8.22
ACTION_LS_QS = 193;
// ACTION: Swipe down to open quick settings when unlocked
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 6.0
- // GMS: 7.8.22
ACTION_SHADE_QS_PULL = 194;
// ACTION: Notification shade > Tap to open quick settings
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 6.0
- // GMS: 7.8.22
ACTION_SHADE_QS_TAP = 195;
// OPEN: Lockscreen
// SUBTYPE: 0 is unsecure, 1 is secured by password / pattern / PIN
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 5.1.1
- // GMS: 7.8.22
LOCKSCREEN = 196;
// OPEN: Lockscreen > Screen to enter password / pattern / PIN
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 5.1.1
- // GMS: 7.8.22
BOUNCER = 197;
// OPEN: Screen turned on
// SUBTYPE: 2 is user action
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 5.1.1
- // GMS: 7.8.22
SCREEN = 198;
// OPEN: Notification caused sound, vibration, and/or LED blink
// SUBTYPE: 1 is buzz, 2 is beep, blink is 4, or'd together
// CATEGORY: NOTIFICATION
// OS: 5.1.1
- // GMS: 7.8.53
NOTIFICATION_ALERT = 199;
// ACTION: Lockscreen > Emergency Call button
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 5.1.1
- // GMS: 7.5.26
ACTION_EMERGENCY_CALL = 200;
// OPEN: Settings > Apps > Configure > Default apps > Assist & voice input
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
APPLICATIONS_MANAGE_ASSIST = 201;
// OPEN: Settings > Memory
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
PROCESS_STATS_SUMMARY = 202;
// ACTION: Settings > Display > When device is rotated
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_ROTATION_LOCK = 203;
// ACTION: Long press on notification to view controls
// CATEGORY: NOTIFICATION
// OS: 6.0
- // GMS: 7.5.26
ACTION_NOTE_CONTROLS = 204;
// ACTION: Notificatoin controls > Info button
// CATEGORY: NOTIFICATION
// OS: 6.0
- // GMS: 7.5.26
ACTION_NOTE_INFO = 205;
// ACTION: Notification controls > Settings button
// CATEGORY: NOTIFICATION
// OS: 6.0
- // GMS: 7.5.26
ACTION_APP_NOTE_SETTINGS = 206;
// OPEN: Volume Dialog (with hardware buttons)
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 6.0
- // GMS: 7.5.26
VOLUME_DIALOG = 207;
// OPEN: Volume dialog > Expanded volume dialog (multiple sliders)
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 6.0
- // GMS: 7.5.26
VOLUME_DIALOG_DETAILS = 208;
// ACTION: Volume dialog > Adjust volume slider
// SUBTYPE: volume level (0-7)
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 6.0
- // GMS: 7.5.26
ACTION_VOLUME_SLIDER = 209;
// ACTION: Volume dialog > Select non-active stream
// SUBTYPE: stream (defined in AudioSystem.java)
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 6.0
- // GMS: 7.5.26
ACTION_VOLUME_STREAM = 210;
// ACTION: Adjust volume with hardware key
// SUBTYPE: volume level (0-7)
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 6.0
- // GMS: 7.5.26
ACTION_VOLUME_KEY = 211;
// ACTION: Volume dialog > Mute a stream by tapping icon
// SUBTYPE: mute is 1, audible is 2
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 6.0
- // GMS: 7.5.26
ACTION_VOLUME_ICON = 212;
// ACTION: Volume dialog > Change ringer mode by tapping icon
// SUBTYPE: 2 is audible, 3 is vibrate
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 6.0
- // GMS: 7.5.26
ACTION_RINGER_MODE = 213;
// ACTION: Chooser shown (share target, file open, etc.)
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 6.0
- // GMS: 7.5.26
ACTION_ACTIVITY_CHOOSER_SHOWN = 214;
// ACTION: Chooser > User taps an app target
// SUBTYPE: Index of target
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 6.0
- // GMS: 7.5.26
ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET = 215;
// ACTION: Chooser > User taps a service target
// SUBTYPE: Index of target
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 6.0
- // GMS: 7.5.26
ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET = 216;
// ACTION: Chooser > User taps a standard target
// SUBTYPE: Index of target
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 6.0
- // GMS: 7.5.26
ACTION_ACTIVITY_CHOOSER_PICKED_STANDARD_TARGET = 217;
// ACTION: QS Brightness Slider (with auto brightness disabled)
// SUBTYPE: slider value
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_BRIGHTNESS = 218;
// ACTION: QS Brightness Slider (with auto brightness enabled)
// SUBTYPE: slider value
// CATEGORY: QUICK_SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_BRIGHTNESS_AUTO = 219;
// OPEN: Settings > Display > Brightness Slider
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
BRIGHTNESS_DIALOG = 220;
// OPEN: Settings > Apps > Configure Apps > Draw over other apps
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
SYSTEM_ALERT_WINDOW_APPS = 221;
// OPEN: Display has entered dream mode
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 6.0
- // GMS: 7.5.26
DREAMING = 222;
// OPEN: Display has entered ambient notification mode
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 6.0
- // GMS: 7.5.26
DOZING = 223;
// OPEN: Overview
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 6.0
- // GMS: 7.5.26
OVERVIEW_ACTIVITY = 224;
// OPEN: Settings > About phone > Legal information
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ABOUT_LEGAL_SETTINGS = 225;
// OPEN: Settings > Search > Perform search
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
ACTION_SEARCH_RESULTS = 226;
// OPEN: Settings > System UI Tuner
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
TUNER = 227;
// OPEN: Settings > System UI Tuner > Quick Settings
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
TUNER_QS = 228;
// OPEN: Settings > System UI Tuner > Demo mode
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
TUNER_DEMO_MODE = 229;
// ACTION: Settings > System UI Tuner > Quick Settings > Move tile
// PACKAGE: Tile
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
TUNER_QS_REORDER = 230;
// ACTION: Settings > System UI Tuner > Quick Settings > Add tile
// PACKAGE: Tile
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
TUNER_QS_ADD = 231;
// ACTION: Settings > System UI Tuner > Quick Settings > Remove tile
// PACKAGE: Tile
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
TUNER_QS_REMOVE = 232;
// ACTION: Settings > System UI Tuner > Status bar > Enable icon
// PACKAGE: Icon
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
TUNER_STATUS_BAR_ENABLE = 233;
// ACTION: Settings > System UI Tuner > Status bar > Disable icon
// PACKAGE: Icon
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
TUNER_STATUS_BAR_DISABLE = 234;
// ACTION: Settings > System UI Tuner > Demo mode > Enable demo mode
// SUBTYPE: false is disabled, true is enabled
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
TUNER_DEMO_MODE_ENABLED = 235;
// ACTION: Settings > System UI Tuner > Demo mode > Show demo mode
// SUBTYPE: false is disabled, true is enabled
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
TUNER_DEMO_MODE_ON = 236;
// ACTION: Settings > System UI Tuner > Show embedded battery percentage
// SUBTYPE: 0 is disabled, 1 is enabled
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
TUNER_BATTERY_PERCENTAGE = 237;
// OPEN: Settings > Developer options > Inactive apps
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.5.26
FUELGAUGE_INACTIVE_APPS = 238;
// ACTION: Long press home to bring up assistant
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 6.0
- // GMS: 7.5.26
ACTION_ASSIST_LONG_PRESS = 239;
// OPEN: Settings > Security > Nexus Imprint > Add Fingerprint
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.8.99
FINGERPRINT_ENROLLING = 240;
// OPEN: Fingerprint Enroll > Find Sensor
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.8.99
FINGERPRINT_FIND_SENSOR = 241;
// OPEN: Fingerprint Enroll > Fingerprint Enrolled!
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.8.99
FINGERPRINT_ENROLL_FINISH = 242;
// OPEN: Fingerprint Enroll introduction
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.8.99
FINGERPRINT_ENROLL_INTRO = 243;
// OPEN: Fingerprint Enroll onboarding
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.8.99
FINGERPRINT_ENROLL_ONBOARD = 244;
// OPEN: Fingerprint Enroll > Let's Start!
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.8.99
FINGERPRINT_ENROLL_SIDECAR = 245;
// OPEN: Fingerprint Enroll SUW > Let's Start!
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.8.99
FINGERPRINT_ENROLLING_SETUP = 246;
// OPEN: Fingerprint Enroll SUW > Find Sensor
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.8.99
FINGERPRINT_FIND_SENSOR_SETUP = 247;
// OPEN: Fingerprint Enroll SUW > Fingerprint Enrolled!
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.8.99
FINGERPRINT_ENROLL_FINISH_SETUP = 248;
// OPEN: Fingerprint Enroll SUW introduction
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.8.99
FINGERPRINT_ENROLL_INTRO_SETUP = 249;
// OPEN: Fingerprint Enroll SUW onboarding
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.8.99
FINGERPRINT_ENROLL_ONBOARD_SETUP = 250;
// ACTION: Add fingerprint > Enroll fingerprint
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.8.99
ACTION_FINGERPRINT_ENROLL = 251;
// ACTION: Authenticate using fingerprint
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.8.99
ACTION_FINGERPRINT_AUTH = 252;
// ACTION: Settings > Security > Nexus Imprint > [Fingerprint] > Delete
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.8.99
ACTION_FINGERPRINT_DELETE = 253;
// ACTION: Settings > Security > Nexus Imprint > [Fingerprint] > Rename
// CATEGORY: SETTINGS
// OS: 6.0
- // GMS: 7.8.99
ACTION_FINGERPRINT_RENAME = 254;
// ACTION: Double tap camera shortcut
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 6.0
- // GMS: 7.8.99
ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE = 255;
// ACTION: Double twist camera shortcut
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: 6.0
- // GMS: 7.8.99
ACTION_WIGGLE_CAMERA_GESTURE = 256;
// OPEN: QS Work Mode tile shown
@@ -1569,13 +1335,11 @@
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: QUICK_SETTINGS
// OS: N
- // GMS: 7.8.99
QS_WORKMODE = 257;
// OPEN: Settings > Developer Options > Background Check
// CATEGORY: SETTINGS
// OS: N
- // GMS: 7.8.99
BACKGROUND_CHECK_SUMMARY = 258;
// OPEN: QS Lock tile shown
@@ -1583,52 +1347,44 @@
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: QUICK_SETTINGS
// OS: N
- // GMS: 7.8.99
QS_LOCK_TILE = 259;
// OPEN: QS User Tile shown
// CATEGORY: QUICK_SETTINGS
// OS: N
- // GMS: 7.8.99
QS_USER_TILE = 260;
// OPEN: QS Battery tile shown
// CATEGORY: QUICK_SETTINGS
// OS: N
- // GMS: 7.8.99
QS_BATTERY_TILE = 261;
// OPEN: Settings > Sound > Do not disturb > Visual interruptions
// CATEGORY: SETTINGS
// OS: N
- // GMS: 7.8.99
NOTIFICATION_ZEN_MODE_VISUAL_INTERRUPTIONS = 262;
// ACTION: Visual interruptions > No screen interuptions toggle
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: SETTINGS
// OS: N
- // GMS: 7.8.99
ACTION_ZEN_ALLOW_WHEN_SCREEN_OFF = 263;
// ACTION: Visual interruptions > No notification light toggle
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: SETTINGS
// OS: N
- // GMS: 7.8.99
ACTION_ZEN_ALLOW_LIGHTS = 264;
// OPEN: Settings > Notifications > [App] > Topic Notifications
// CATEGORY: SETTINGS
// OS: N
- // GMS: 7.8.99
NOTIFICATION_TOPIC_NOTIFICATION = 265;
// ACTION: Settings > Apps > Default Apps > Select different SMS app
// PACKAGE: Selected SMS app
// CATEGORY: SETTINGS
// OS: N
- // GMS: 7.8.99
ACTION_DEFAULT_SMS_APP_CHANGED = 266;
// OPEN: QS Color modification tile shown
@@ -1636,105 +1392,88 @@
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: QUICK_SETTINGS
// OS: N
- // GMS: 7.8.99
QS_COLOR_MATRIX = 267;
// OPEN: QS Custom tile shown
// ACTION: QS Work Mode tile tapped
// CATEGORY: QUICK_SETTINGS
// OS: N
- // GMS: 7.8.99
QS_CUSTOM = 268;
// ACTION: Visual interruptions > Never turn off the screen toggle
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: SETTINGS
// OS: N
- // GMS: 7.8.99
ACTION_ZEN_ALLOW_WHEN_SCREEN_ON = 269;
// ACTION: Overview > Long-press task, drag to enter split-screen
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: N
- // GMS: 7.8.99
ACTION_WINDOW_DOCK_DRAG_DROP = 270;
// ACTION: In App > Long-press Overview button to enter split-screen
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: N
- // GMS: 7.8.99
ACTION_WINDOW_DOCK_LONGPRESS = 271;
// ACTION: In App > Swipe Overview button to enter split-screen
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: N
- // GMS: 7.8.99
ACTION_WINDOW_DOCK_SWIPE = 272;
// ACTION: Launch profile-specific app > Confirm credentials
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: N
- // GMS: 7.8.99
PROFILE_CHALLENGE = 273;
// OPEN: QS Battery detail panel
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: N
- // GMS: 7.8.99
QS_BATTERY_DETAIL = 274;
// OPEN: Overview > History
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: N
- // GMS: 7.8.99
OVERVIEW_HISTORY = 275;
// ACTION: Overview > Page by tapping Overview button
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: N
- // GMS: 7.8.99
ACTION_OVERVIEW_PAGE = 276;
// ACTION: Overview > Select app
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: N
- // GMS: 7.8.99
ACTION_OVERVIEW_SELECT = 277;
// ACTION: View emergency info
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: N
- // GMS: 7.8.99
ACTION_VIEW_EMERGENCY_INFO = 278;
// ACTION: Edit emergency info activity
// CATEGORY: SETTINGS
// OS: N
- // GMS: 7.8.99
ACTION_EDIT_EMERGENCY_INFO = 279;
// ACTION: Edit emergency info field
// CATEGORY: SETTINGS
// OS: N
- // GMS: 7.8.99
ACTION_EDIT_EMERGENCY_INFO_FIELD = 280;
// ACTION: Add emergency contact
// CATEGORY: SETTINGS
// OS: N
- // GMS: 7.8.99
ACTION_ADD_EMERGENCY_CONTACT = 281;
// ACTION: Delete emergency contact
// CATEGORY: SETTINGS
// OS: N
- // GMS: 7.8.99
ACTION_DELETE_EMERGENCY_CONTACT = 282;
// ACTION: Call emergency contact
// CATEGORY: SETTINGS
// OS: N
- // GMS: 7.8.99
ACTION_CALL_EMERGENCY_CONTACT = 283;
// OPEN: QS Data Saver tile shown
@@ -1745,13 +1484,11 @@
// OPEN: Settings > Security > User credentials
// CATEGORY: Settings
// OS: N
- // GMS: 7.8.99
USER_CREDENTIALS = 285;
// ACTION: In App (splitscreen) > Long-press Overview to exit split-screen
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: N
- // GMS: 7.8.99
ACTION_WINDOW_UNDOCK_LONGPRESS = 286;
// Logged when the user scrolls through overview manually
@@ -1773,81 +1510,68 @@
// ACTION: Long-press power button, then tap "Take bug report" option.
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: N
- // GMS: 7.8.99
ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE = 292;
// ACTION: Long-press power button, then long-press "Take bug report" option.
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: N
- // GMS: 7.8.99
ACTION_BUGREPORT_FROM_POWER_MENU_FULL = 293;
// ACTION: Settings -> Developer Options -> Take bug report -> Interactive report
// CATEGORY: SETTINGS
// OS: N
- // GMS: 7.8.99
// Interactive bug report initiated from Settings.
ACTION_BUGREPORT_FROM_SETTINGS_INTERACTIVE = 294;
// ACTION: Settings -> Developer Options -> Take bug report -> Full report
// CATEGORY: SETTINGS
// OS: N
- // GMS: 7.8.99
// Interactive bug report initiated from Settings.
ACTION_BUGREPORT_FROM_SETTINGS_FULL = 295;
// ACTION: User tapped notification action to cancel a bug report
// CATEGORY: NOTIFICATION
// OS: N
- // GMS: 7.8.99
ACTION_BUGREPORT_NOTIFICATION_ACTION_CANCEL = 296;
// ACTION: User tapped notification action to launch bug report details screen
// CATEGORY: NOTIFICATION
// OS: N
- // GMS: 7.8.99
ACTION_BUGREPORT_NOTIFICATION_ACTION_DETAILS = 297;
// ACTION: User tapped notification action to take adition screenshot on bug report
// CATEGORY: NOTIFICATION
// OS: N
- // GMS: 7.8.99
ACTION_BUGREPORT_NOTIFICATION_ACTION_SCREENSHOT = 298;
// ACTION: User tapped notification to share bug report
// CATEGORY: NOTIFICATION
// OS: N
- // GMS: 7.8.99
ACTION_BUGREPORT_NOTIFICATION_ACTION_SHARE = 299;
// ACTION: User changed bug report name using the details screen
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: N
- // GMS: 7.8.99
ACTION_BUGREPORT_DETAILS_NAME_CHANGED = 300;
// ACTION: User changed bug report title using the details screen
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: N
- // GMS: 7.8.99
ACTION_BUGREPORT_DETAILS_TITLE_CHANGED = 301;
// ACTION: User changed bug report description using the details screen
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: N
- // GMS: 7.8.99
ACTION_BUGREPORT_DETAILS_DESCRIPTION_CHANGED = 302;
// ACTION: User tapped Save in the bug report details screen.
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: N
- // GMS: 7.8.99
ACTION_BUGREPORT_DETAILS_SAVED = 303;
// ACTION: User tapped Cancel in the bug report details screen.
// CATEGORY: GLOBAL_SYSTEM_UI
// OS: N
- // GMS: 7.8.99
ACTION_BUGREPORT_DETAILS_CANCELED = 304;
// Tuner: Open/close calibrate dialog.
@@ -1920,79 +1644,140 @@
// the transition was executed.
APP_TRANSITION_DEVICE_UPTIME_SECONDS = 325;
- // User granted access to the request folder; action takes an integer
- // representing the folder's index on Environment.STANDARD_DIRECTORIES
- // (or -2 for root access, or -1 or unknown directory).
+ // ACTION: app requested access to a scoped directory, user granted it.
+ // SUBTYPE: directory's index on Environment.STANDARD_DIRECTORIES
+ // CATEGORY: GLOBAL_SYSTEM_UI
+ // OS: N
ACTION_SCOPED_DIRECTORY_ACCESS_GRANTED_BY_FOLDER = 326;
- // User denied access to the request folder; action takes an integer
- // representing the folder's index on Environment.STANDARD_DIRECTORIES
- // (or -2 for root access, or -1 or unknown directory).
+ // ACTION: app requested access to a scoped directory, user denied it.
+ // SUBTYPE: directory's index on Environment.STANDARD_DIRECTORIES
+ // CATEGORY: GLOBAL_SYSTEM_UI
+ // OS: N
ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_BY_FOLDER = 327;
- // User granted access to the request folder; action pass package name
- // of calling package.
+ // ACTION: app requested access to a scoped directory, user granted it.
+ // PACKAGE: app that requested access
+ // CATEGORY: GLOBAL_SYSTEM_UI
+ // OS: N
ACTION_SCOPED_DIRECTORY_ACCESS_GRANTED_BY_PACKAGE = 328;
- // User denied access to the request folder; action pass package name
- // of calling package.
+ // ACTION: app requested access to a scoped directory, user denied it.
+ // PACKAGE: app that requested access.
+ // CATEGORY: GLOBAL_SYSTEM_UI
+ // OS: N
ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_BY_PACKAGE = 329;
- // App requested access to a directory it has already been granted
- // access before; action takes an integer representing the folder's
- // index on Environment.STANDARD_DIRECTORIES
- // (or -2 for root access, or -1 or unknown directory).
+ // ACTION: app requested access to a directory user has already been granted
+ // access before.
+ // SUBTYPE: directory's index on Environment.STANDARD_DIRECTORIES.
+ // CATEGORY: GLOBAL_SYSTEM_UI
+ // OS: N
ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED_BY_FOLDER = 330;
- // App requested access to a directory it has already been granted
- // access before; action pass package name of calling package.
+ // ACTION: app requested access to a directory user has already been granted
+ // access before.
+ // PACKAGE: app that requested access.
+ // CATEGORY: GLOBAL_SYSTEM_UI
+ // OS: N
ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED_BY_PACKAGE = 331;
- // Logged when the user slides a notification and
- // reveals the gear beneath it.
+ // ACTION: Logged when the user slides a notification and reveals the gear
+ // beneath it.
+ // CATEGORY: NOTIFICATION
+ // OS: N
ACTION_REVEAL_GEAR = 332;
- // Logged when the user taps on the gear beneath
- // a notification.
+ // ACTION: Logged when the user taps on the gear beneath a notification.
+ // CATEGORY: NOTIFICATION
+ // OS: N
ACTION_TOUCH_GEAR = 333;
// Logs that the user has edited the enabled VR listeners.
+ // CATEGORY: SETTINGS
+ // OS: N
VR_MANAGE_LISTENERS = 334;
// Settings -> Accessibility -> Click after pointer stops moving
+ // CATEGORY: SETTINGS
+ // OS: N
ACCESSIBILITY_TOGGLE_AUTOCLICK = 335;
+
// Settings -> Sound
+ // CATEGORY: SETTINGS
+ // OS: N
SOUND = 336;
+
// Settings -> Notifications -> Gear
+ // CATEGORY: SETTINGS
+ // OS: N
CONFIGURE_NOTIFICATION = 337;
+
// Settings -> Wi-Fi -> Gear
+ // CATEGORY: SETTINGS
+ // OS: N
CONFIGURE_WIFI = 338;
+
// Settings -> Display -> Display size
+ // OS: N
DISPLAY_SCREEN_ZOOM = 339;
+
// Settings -> Display -> Font size
+ // CATEGORY: SETTINGS
+ // OS: N
ACCESSIBILITY_FONT_SIZE = 340;
+
// Settings -> Data usage -> Cellular/Wi-Fi data usage
+ // CATEGORY: SETTINGS
+ // OS: N
DATA_USAGE_LIST = 341;
+
// Settings -> Data usage -> Billing cycle or DATA_USAGE_LIST -> Gear
+ // CATEGORY: SETTINGS
+ // OS: N
BILLING_CYCLE = 342;
+
// DATA_USAGE_LIST -> Any item or App info -> Data usage
+ // CATEGORY: SETTINGS
+ // OS: N
APP_DATA_USAGE = 343;
+
// Settings -> Language & input -> Language
+ // CATEGORY: SETTINGS
+ // OS: N
USER_LOCALE_LIST = 344;
+
// Settings -> Language & input -> Virtual keyboard
+ // CATEGORY: SETTINGS
+ // OS: N
VIRTUAL_KEYBOARDS = 345;
+
// Settings -> Language & input -> Physical keyboard
+ // CATEGORY: SETTINGS
+ // OS: N
PHYSICAL_KEYBOARDS = 346;
+
// Settings -> Language & input -> Virtual keyboard -> Add a virtual keyboard
+ // CATEGORY: SETTINGS
+ // OS: N
ENABLE_VIRTUAL_KEYBOARDS = 347;
+
// Settings -> Data usage -> Data Saver
+ // CATEGORY: SETTINGS
+ // OS: N
DATA_SAVER_SUMMARY = 348;
+
// Settings -> Data usage -> Data Saver -> Unrestricted data access
+ // CATEGORY: SETTINGS
+ // OS: N
DATA_USAGE_UNRESTRICTED_ACCESS = 349;
// Used for generic logging of Settings Preference Persistence, should not be used
// outside SharedPreferencesLogger.
+ // CATEGORY: SETTINGS
+ // OS: N
ACTION_GENERIC_PACKAGE = 350;
+
// Settings -> Apps -> Gear -> Special access
SPECIAL_ACCESS = 351;
@@ -2158,15 +1943,28 @@
// System UI Tuner > Other > Power notification controls > Toggle on/off
ACTION_TUNER_POWER_NOTIFICATION_CONTROLS = 393;
- // Action: user enable / disabled data saver using Settings. Arguments:
- // 0: Data Saver mode is disabled.
- // 1: Data Saver mode is enabled.
+ // Action: user enable / disabled data saver using Settings
+ // OPEN: Settings -> Data Usage -> Data saver -> On/off toggle
+ // VALUE: 1 for enabled, 0 for disabled
+ // CATEGORY: SETTINGS
+ // OS: N
ACTION_DATA_SAVER_MODE = 394;
- // User whitelisted an app for Data Saver mode; action pass package name of app.
+ // User whitelisted an app for Data Saver mode; action pass package name of app
+ // Action: user enable / disabled data saver using Settings
+ // OPEN: Settings -> Data Usage -> Data saver -> Unrestricted data access > APP toggle turned on
+ // or
+ // Settings -> Apps -> APP -> Data usage -> Unrestricted data usage toggle turned on
+ // VALUE: package name of APP
+ // CATEGORY: SETTINGS
+ // OS: N
ACTION_DATA_SAVER_WHITELIST = 395;
- // User blacklisted an app for Data Saver mode; action pass package name of app.
+ // User blacklisted an app for Data Saver mode; action pass package name of app
+ // OPEN: Settings -> Apps -> APP -> Data usage -> Background data toggle turned off
+ // VALUE: package name of APP
+ // CATEGORY: SETTINGS
+ // OS: N
ACTION_DATA_SAVER_BLACKLIST = 396;
// User opened a remote input view associated with a notification. Passes package name of app
@@ -2212,12 +2010,204 @@
// Notification group expansion state toggled by the expand affordance.
ACTION_NOTIFICATION_GROUP_EXPANDER = 408;
+
// Notification expansion state toggled by the expand gesture.
ACTION_NOTIFICATION_GESTURE_EXPANDER = 409;
// Notification group expansion state toggled by the expand gesture.
ACTION_NOTIFICATION_GROUP_GESTURE_EXPANDER = 410;
+ // User performs gesture that activates the ambient display
+ // 1: Gesture performed is Nudge
+ // 2: Gesture performed is Pickup
+ // 4: Gesture performed is Double Tap
+ ACTION_AMBIENT_GESTURE = 411;
+
+ // ---- End N Constants, all N constants go above this line ----
+
+ // ------- Begin N App Disambig Shade -----
+ // Application disambig shade opened or closed with a featured app.
+ // These are actually visibility events, but visible/hidden doesn't
+ // take a package, so these are being logged as actions.
+ // Package: Calling app on open, called app on close
+ ACTION_SHOW_APP_DISAMBIG_APP_FEATURED = 451;
+ ACTION_HIDE_APP_DISAMBIG_APP_FEATURED = 452;
+
+ // Application disambig shade opened or closed without a featured app.
+ // These are actually visibility events, but visible/hidden doesn't
+ // take a package, so these are being logged as actions.
+ // Package: Calling app on open, called app on close
+ ACTION_SHOW_APP_DISAMBIG_NONE_FEATURED = 453;
+ ACTION_HIDE_APP_DISAMBIG_NONE_FEATURED = 454;
+
+ // User opens in an app by pressing “Always” in the application disambig shade.
+ // Subtype: Index of selection
+ ACTION_APP_DISAMBIG_ALWAYS = 455;
+
+ // User opens in an app by pressing “Just Once” in the application disambig shade.
+ // Subtype: Index of selection
+ ACTION_APP_DISAMBIG_JUST_ONCE = 456;
+
+ // User opens in an app by tapping on its name in the application disambig shade.
+ // Subtype: Index of selection
+ ACTION_APP_DISAMBIG_TAP = 457;
+
+ // OPEN: Settings > Internal storage > Storage manager
+ // CATEGORY: SETTINGS
+ STORAGE_MANAGER_SETTINGS = 458;
+
+ // OPEN: Settings -> Gestures
+ // CATEGORY: SETTINGS
+ SETTINGS_GESTURES = 459;
+
+ // ------ Begin Deletion Helper ------
+ // ACTION: Settings > Storage > Free Up Space > Photos & Videos > Toggle
+ // SUBTYPE: false is off, true is on
+ // CATEGORY: SETTINGS
+ ACTION_DELETION_SELECTION_PHOTOS = 460;
+
+ // ACTION: Settings > Storage > Free Up Space > Apps > Toggle
+ // SUBTYPE: false is off, true is on
+ // CATEGORY: SETTINGS
+ ACTION_DELETION_SELECTION_ALL_APPS = 461;
+
+ // ACTION: Settings > Storage > Free Up Space > Apps > Click an unchecked app
+ // CATEGORY: SETTINGS
+ // PACKAGE: Unchecked app
+ ACTION_DELETION_SELECTION_APP_ON = 462;
+
+ // ACTION: Settings > Storage > Free Up Space > Apps > Click a checked app
+ // CATEGORY: SETTINGS
+ // PACKAGE: Checked app
+ ACTION_DELETION_SELECTION_APP_OFF = 463;
+
+ // ACTION: Settings > Storage > Free Up Space > Apps > Click category
+ // SUBTYPE: false is expanded, true is collapsed
+ // CATEGORY: SETTINGS
+ ACTION_DELETION_APPS_COLLAPSED = 464;
+
+ // ACTION: Settings > Storage > Free Up Space > Downloads > Check On
+ // SUBTYPE: false is off, true is on
+ // CATEGORY: SETTINGS
+ ACTION_DELETION_SELECTION_DOWNLOADS = 465;
+
+ // ACTION: Settings > Storage > Free Up Space > Downloads > Click category
+ // SUBTYPE: false is expanded, true is collapsed
+ // CATEGORY: SETTINGS
+ ACTION_DELETION_DOWNLOADS_COLLAPSED = 466;
+
+ // ACTION: Settings > Storage > Free Up Space > Free up ... GB
+ // CATEGORY: SETTINGS
+ ACTION_DELETION_HELPER_CLEAR = 467;
+
+ // ACTION: Settings > Storage > Free Up Space > Cancel
+ // CATEGORY: SETTINGS
+ ACTION_DELETION_HELPER_CANCEL = 468;
+
+ // ACTION: Settings > Storage > Free Up Space > Free up ... GB > Remove
+ // CATEGORY: SETTINGS
+ ACTION_DELETION_HELPER_REMOVE_CONFIRM = 469;
+
+ // ACTION: Settings > Storage > Free Up Space > Free up ... GB > Cancel
+ // CATEGORY: SETTINGS
+ ACTION_DELETION_HELPER_REMOVE_CANCEL = 470;
+
+ // Deletion helper encountered an error during package deletion.
+ ACTION_DELETION_HELPER_APPS_DELETION_FAIL = 471;
+
+ // Deletion helper encountered an error during downloads folder deletion.
+ ACTION_DELETION_HELPER_DOWNLOADS_DELETION_FAIL = 472;
+
+ // Deletion helper encountered an error during photo and video deletion.
+ ACTION_DELETION_HELPER_PHOTOS_VIDEOS_DELETION_FAIL = 473;
+
+ // OPEN: Settings (root page if there are multiple tabs)
+ // CATEGORY: SETTINGS
+ DASHBOARD_CONTAINER = 474;
+
+ // OPEN: Settings -> SUPPORT TAB
+ // CATEGORY: SETTINGS
+ SUPPORT_FRAGMENT = 475;
+
+ // ACTION: Settings -> Select summary tab.
+ // CATEGORY: SETTINGS
+ ACTION_SELECT_SUMMARY=476;
+
+ // ACTION: Settings -> Select support tab.
+ // CATEGORY: SETTINGS
+ ACTION_SELECT_SUPPORT_FRAGMENT = 477;
+
+ // ACTION: Settings -> Support -> Tips & tricks
+ // CATEGORY: SETTINGS
+ ACTION_SUPPORT_TIPS_AND_TRICKS = 478;
+
+ // ACTION: Settings -> Support -> Help & feedback
+ // CATEGORY: SETTINGS
+ ACTION_SUPPORT_HELP_AND_FEEDBACK = 479;
+
+ // ACTION: Settings -> Support -> Sign in
+ // CATEGORY: SETTINGS
+ ACTION_SUPPORT_SIGN_IN = 480;
+
+ // ACTION: Settings -> Support -> Phone
+ // CATEGORY: SETTINGS
+ ACTION_SUPPORT_PHONE = 481;
+
+ // ACTION: Settings -> Support -> Chat
+ // CATEGORY: SETTINGS
+ ACTION_SUPPORT_CHAT = 482;
+
+ // ACTION: Settings -> Support -> Phone/Chat -> Disclaimer Cancel
+ // CATEGORY: SETTINGS
+ ACTION_SUPPORT_DISCLAIMER_CANCEL = 483;
+
+ // ACTION: Settings -> Support -> Phone/Chat -> Disclaimer OK
+ // CATEGORY: SETTINGS
+ ACTION_SUPPORT_DISCLAIMER_OK = 484;
+
+ // ACTION: Settings -> Support -> Toll-Free Phone
+ // CATEGORY: SETTINGS
+ ACTION_SUPPORT_DAIL_TOLLFREE = 485;
+
+ // ACTION: Settings -> Support -> "Travel Abroad" Button
+ // CATEGORY: SETTINGS
+ ACTION_SUPPORT_VIEW_TRAVEL_ABROAD_DIALOG = 486;
+
+ // ACTION: Settings -> Support -> "Travel Abroad" Button -> Tolled Phone
+ // CATEGORY: SETTINGS
+ ACTION_SUPPORT_DIAL_TOLLED = 487;
+
+ // OPEN: Settings > Display > Night Light
+ // CATEGORY: SETTINGS
+ NIGHT_DISPLAY_SETTINGS = 488;
+
+ // ACTION: Settings -> Storage -> Manage storage -> Click Storage Manager
+ // SUBTYPE: false is off, true is on
+ ACTION_TOGGLE_STORAGE_MANAGER = 489;
+
+ // Settings launched from collapsed quick settings.
+ ACTION_QS_COLLAPSED_SETTINGS_LAUNCH = 490;
+
+ // OPEN: QS Night Light tile shown
+ // ACTION: QS Night Light tile tapped
+ // SUBTYPE: 0 is off, 1 is on
+ // CATEGORY: QUICK_SETTINGS
+ QS_NIGHT_DISPLAY = 491;
+
+ // Night Light on
+ SETTINGS_CONDITION_NIGHT_DISPLAY = 492;
+
+ // System navigation key up.
+ ACTION_SYSTEM_NAVIGATION_KEY_UP = 493;
+
+ // System navigation key down.
+ ACTION_SYSTEM_NAVIGATION_KEY_DOWN = 494;
+
+ // OPEN: Settings > Display -> Ambient Display
+ // CATEGORY: SETTINGS
+ ACTION_AMBIENT_DISPLAY = 495;
+
+ // ---- End N-MR1 Constants, all N-MR1 constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/services/Android.mk b/services/Android.mk
index 1918db5..3385bed 100644
--- a/services/Android.mk
+++ b/services/Android.mk
@@ -28,6 +28,7 @@
net \
print \
restrictions \
+ retaildemo \
usage \
usb \
voiceinteraction
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 2a7d945..fd93865 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -77,6 +77,9 @@
*/
static final int FLAG_FEATURE_INJECT_MOTION_EVENTS = 0x00000010;
+ static final int FEATURES_AFFECTING_MOTION_EVENTS = FLAG_FEATURE_INJECT_MOTION_EVENTS
+ | FLAG_FEATURE_AUTOCLICK | FLAG_FEATURE_TOUCH_EXPLORATION
+ | FLAG_FEATURE_SCREEN_MAGNIFIER;
/**
* Flag for enabling the feature to control the screen magnifier. If
* {@link #FLAG_FEATURE_SCREEN_MAGNIFIER} is set this flag is ignored
@@ -203,8 +206,13 @@
}
if (event instanceof MotionEvent) {
- MotionEvent motionEvent = (MotionEvent) event;
- processMotionEvent(state, motionEvent, policyFlags);
+ if ((mEnabledFeatures & FEATURES_AFFECTING_MOTION_EVENTS) != 0) {
+ MotionEvent motionEvent = (MotionEvent) event;
+ processMotionEvent(state, motionEvent, policyFlags);
+ return;
+ } else {
+ super.onInputEvent(event, policyFlags);
+ }
} else if (event instanceof KeyEvent) {
KeyEvent keyEvent = (KeyEvent) event;
processKeyEvent(state, keyEvent, policyFlags);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index f039e1e..695ea60 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -279,6 +279,31 @@
}
@Override
+ public void onPackageUpdateFinished(String packageName, int uid) {
+ // Unbind all services from this package, and then update the user state to
+ // re-bind new versions of them.
+ synchronized (mLock) {
+ final int userId = getChangingUserId();
+ if (userId != mCurrentUserId) {
+ return;
+ }
+ UserState userState = getUserStateLocked(userId);
+ boolean unboundAService = false;
+ for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
+ Service boundService = userState.mBoundServices.get(i);
+ String servicePkg = boundService.mComponentName.getPackageName();
+ if (servicePkg.equals(packageName)) {
+ boundService.unbindLocked();
+ unboundAService = true;
+ }
+ }
+ if (unboundAService) {
+ onUserStateChangedLocked(userState);
+ }
+ }
+ }
+
+ @Override
public void onPackageRemoved(String packageName, int uid) {
synchronized (mLock) {
final int userId = getChangingUserId();
@@ -1112,9 +1137,11 @@
private void addServiceLocked(Service service, UserState userState) {
try {
- service.onAdded();
- userState.mBoundServices.add(service);
- userState.mComponentNameToServiceMap.put(service.mComponentName, service);
+ if (!userState.mBoundServices.contains(service)) {
+ service.onAdded();
+ userState.mBoundServices.add(service);
+ userState.mComponentNameToServiceMap.put(service.mComponentName, service);
+ }
} catch (RemoteException re) {
/* do nothing */
}
@@ -1127,8 +1154,14 @@
*/
private void removeServiceLocked(Service service, UserState userState) {
userState.mBoundServices.remove(service);
- userState.mComponentNameToServiceMap.remove(service.mComponentName);
service.onRemoved();
+ // It may be possible to bind a service twice, which confuses the map. Rebuild the map
+ // to make sure we can still reach a service
+ userState.mComponentNameToServiceMap.clear();
+ for (int i = 0; i < userState.mBoundServices.size(); i++) {
+ Service boundService = userState.mBoundServices.get(i);
+ userState.mComponentNameToServiceMap.put(boundService.mComponentName, boundService);
+ }
}
/**
@@ -1423,7 +1456,8 @@
updateTouchExplorationLocked(userState);
updatePerformGesturesLocked(userState);
updateEnhancedWebAccessibilityLocked(userState);
- updateDisplayColorAdjustmentSettingsLocked(userState);
+ updateDisplayDaltonizerLocked(userState);
+ updateDisplayInversionLocked(userState);
updateMagnificationLocked(userState);
updateSoftKeyboardShowModeLocked(userState);
scheduleUpdateInputFilter(userState);
@@ -1540,7 +1574,6 @@
somethingChanged |= readEnhancedWebAccessibilityEnabledChangedLocked(userState);
somethingChanged |= readDisplayMagnificationEnabledSettingLocked(userState);
somethingChanged |= readAutoclickEnabledSettingLocked(userState);
- somethingChanged |= readDisplayColorAdjustmentSettingsLocked(userState);
return somethingChanged;
}
@@ -1603,18 +1636,6 @@
return false;
}
- private boolean readDisplayColorAdjustmentSettingsLocked(UserState userState) {
- final boolean displayAdjustmentsEnabled = DisplayAdjustmentUtils.hasAdjustments(mContext,
- userState.mUserId);
- if (displayAdjustmentsEnabled != userState.mHasDisplayColorAdjustment) {
- userState.mHasDisplayColorAdjustment = displayAdjustmentsEnabled;
- return true;
- }
- // If display adjustment is enabled, always assume there was a change in
- // the adjustment settings.
- return displayAdjustmentsEnabled;
- }
-
private boolean readHighTextContrastEnabledSettingLocked(UserState userState) {
final boolean highTextContrastEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
@@ -1731,8 +1752,12 @@
return false;
}
- private void updateDisplayColorAdjustmentSettingsLocked(UserState userState) {
- DisplayAdjustmentUtils.applyAdjustments(mContext, userState.mUserId);
+ private void updateDisplayDaltonizerLocked(UserState userState) {
+ DisplayAdjustmentUtils.applyDaltonizerSetting(mContext, userState.mUserId);
+ }
+
+ private void updateDisplayInversionLocked(UserState userState) {
+ DisplayAdjustmentUtils.applyInversionSetting(mContext, userState.mUserId);
}
private void updateMagnificationLocked(UserState userState) {
@@ -2333,15 +2358,12 @@
}
/**
- * Unbinds form the accessibility service and removes it from the data
+ * Unbinds from the accessibility service and removes it from the data
* structures for service management.
*
* @return True if unbinding is successful.
*/
public boolean unbindLocked() {
- if (mService == null) {
- return false;
- }
UserState userState = getUserStateLocked(mUserId);
getKeyEventDispatcher().flush(this);
if (!mIsAutomation) {
@@ -3049,7 +3071,7 @@
@Override
public void onServiceDisconnected(ComponentName componentName) {
- /* do nothing - #binderDied takes care */
+ binderDied();
}
public void onAdded() throws RemoteException {
@@ -3078,14 +3100,18 @@
}
public void unlinkToOwnDeathLocked() {
- mService.unlinkToDeath(this, 0);
+ if (mService != null) {
+ mService.unlinkToDeath(this, 0);
+ }
}
public void resetLocked() {
try {
// Clear the proxy in the other process so this
// IAccessibilityServiceConnection can be garbage collected.
- mServiceInterface.init(null, mId, null);
+ if (mServiceInterface != null) {
+ mServiceInterface.init(null, mId, null);
+ }
} catch (RemoteException re) {
/* ignore */
}
@@ -3109,10 +3135,10 @@
mWasConnectedAndDied = true;
getKeyEventDispatcher().flush(this);
UserState userState = getUserStateLocked(mUserId);
- // The death recipient is unregistered in removeServiceLocked
- removeServiceLocked(this, userState);
resetLocked();
if (mIsAutomation) {
+ // This is typically done when unbinding, but UiAutomation isn't bound.
+ removeServiceLocked(this, userState);
// We no longer have an automation service, so restore
// the state based on values in the settings database.
userState.mInstalledServices.remove(mAccessibilityServiceInfo);
@@ -3567,6 +3593,7 @@
case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL:
case WindowManager.LayoutParams.TYPE_BASE_APPLICATION:
+ case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION:
case WindowManager.LayoutParams.TYPE_PHONE:
case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
case WindowManager.LayoutParams.TYPE_TOAST:
@@ -4192,7 +4219,6 @@
public boolean mIsAutoclickEnabled;
public boolean mIsPerformGesturesEnabled;
public boolean mIsFilterKeyEventsEnabled;
- public boolean mHasDisplayColorAdjustment;
public boolean mAccessibilityFocusOnlyInActiveWindow;
private Service mUiAutomationService;
@@ -4308,9 +4334,6 @@
private final Uri mDisplayDaltonizerUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER);
- private final Uri mDisplayColorMatrixUri = Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX);
-
private final Uri mHighTextContrastUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED);
@@ -4342,8 +4365,6 @@
contentResolver.registerContentObserver(
mDisplayDaltonizerUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
- mDisplayColorMatrixUri, false, this, UserHandle.USER_ALL);
- contentResolver.registerContentObserver(
mHighTextContrastUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
mAccessibilitySoftKeyboardModeUri, false, this, UserHandle.USER_ALL);
@@ -4385,14 +4406,11 @@
if (readEnhancedWebAccessibilityEnabledChangedLocked(userState)) {
onUserStateChangedLocked(userState);
}
- } else if (mDisplayInversionEnabledUri.equals(uri)
- || mDisplayDaltonizerEnabledUri.equals(uri)
+ } else if (mDisplayDaltonizerEnabledUri.equals(uri)
|| mDisplayDaltonizerUri.equals(uri)) {
- if (readDisplayColorAdjustmentSettingsLocked(userState)) {
- updateDisplayColorAdjustmentSettingsLocked(userState);
- }
- } else if (mDisplayColorMatrixUri.equals(uri)) {
- updateDisplayColorAdjustmentSettingsLocked(userState);
+ updateDisplayDaltonizerLocked(userState);
+ } else if (mDisplayInversionEnabledUri.equals(uri)) {
+ updateDisplayInversionLocked(userState);
} else if (mHighTextContrastUri.equals(uri)) {
if (readHighTextContrastEnabledSettingLocked(userState)) {
onUserStateChangedLocked(userState);
diff --git a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java b/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
index e1f3cd8..1532946 100644
--- a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
+++ b/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
@@ -18,23 +18,23 @@
import android.content.ContentResolver;
import android.content.Context;
-import android.opengl.Matrix;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.provider.Settings;
-import android.util.Slog;
+import android.provider.Settings.Secure;
import android.view.accessibility.AccessibilityManager;
+import com.android.server.LocalServices;
+import com.android.server.display.DisplayTransformManager;
+
/**
* Utility methods for performing accessibility display adjustments.
*/
class DisplayAdjustmentUtils {
- private static final String LOG_TAG = DisplayAdjustmentUtils.class.getSimpleName();
+
+ /** Default inversion mode for display color correction. */
+ private static final int DEFAULT_DISPLAY_DALTONIZER =
+ AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY;
/** Matrix and offset used for converting color to gray-scale. */
- private static final float[] GRAYSCALE_MATRIX = new float[] {
+ private static final float[] MATRIX_GRAYSCALE = new float[] {
.2126f, .2126f, .2126f, 0,
.7152f, .7152f, .7152f, 0,
.0722f, .0722f, .0722f, 0,
@@ -48,150 +48,44 @@
* represents a non-multiplied addition, see surfaceflinger's ProgramCache
* for full implementation details.
*/
- private static final float[] INVERSION_MATRIX_VALUE_ONLY = new float[] {
+ private static final float[] MATRIX_INVERT_COLOR = new float[] {
0.402f, -0.598f, -0.599f, 0,
-1.174f, -0.174f, -1.175f, 0,
-0.228f, -0.228f, 0.772f, 0,
1, 1, 1, 1
};
- /** Default inversion mode for display color correction. */
- private static final int DEFAULT_DISPLAY_DALTONIZER =
- AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY;
-
- /**
- * Returns whether the specified user with has any display color
- * adjustments.
- */
- public static boolean hasAdjustments(Context context, int userId) {
+ public static void applyDaltonizerSetting(Context context, int userId) {
final ContentResolver cr = context.getContentResolver();
+ final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
- if (Settings.Secure.getIntForUser(cr,
- Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, userId) != 0) {
- return true;
+ int daltonizerMode = AccessibilityManager.DALTONIZER_DISABLED;
+ if (Secure.getIntForUser(cr,
+ Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, userId) != 0) {
+ daltonizerMode = Secure.getIntForUser(cr,
+ Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, DEFAULT_DISPLAY_DALTONIZER, userId);
}
- if (Settings.Secure.getIntForUser(cr,
- Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, userId) != 0) {
- return true;
+ float[] grayscaleMatrix = null;
+ if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) {
+ // Monochromacy isn't supported by the native Daltonizer.
+ grayscaleMatrix = MATRIX_GRAYSCALE;
+ daltonizerMode = AccessibilityManager.DALTONIZER_DISABLED;
}
-
- return false;
+ dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE, grayscaleMatrix);
+ dtm.setDaltonizerMode(daltonizerMode);
}
/**
* Applies the specified user's display color adjustments.
*/
- public static void applyAdjustments(Context context, int userId) {
+ public static void applyInversionSetting(Context context, int userId) {
final ContentResolver cr = context.getContentResolver();
- float[] colorMatrix = null;
+ final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
- if (Settings.Secure.getIntForUser(cr,
- Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, userId) != 0) {
- colorMatrix = multiply(colorMatrix, INVERSION_MATRIX_VALUE_ONLY);
- }
-
- if (Settings.Secure.getIntForUser(cr,
- Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, userId) != 0) {
- final int daltonizerMode = Settings.Secure.getIntForUser(cr,
- Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, DEFAULT_DISPLAY_DALTONIZER,
- userId);
- // Monochromacy isn't supported by the native Daltonizer.
- if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) {
- colorMatrix = multiply(colorMatrix, GRAYSCALE_MATRIX);
- setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED);
- } else {
- setDaltonizerMode(daltonizerMode);
- }
- } else {
- setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED);
- }
-
- String matrix = Settings.Secure.getStringForUser(cr,
- Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, userId);
- if (matrix != null) {
- final float[] userMatrix = get4x4Matrix(matrix);
- if (userMatrix != null) {
- colorMatrix = multiply(colorMatrix, userMatrix);
- }
- }
-
- setColorTransform(colorMatrix);
+ final boolean invertColors = Secure.getIntForUser(cr,
+ Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, userId) != 0;
+ dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_INVERT_COLOR,
+ invertColors ? MATRIX_INVERT_COLOR : null);
}
-
- private static float[] get4x4Matrix(String matrix) {
- String[] strValues = matrix.split(",");
- if (strValues.length != 16) {
- return null;
- }
- float[] values = new float[strValues.length];
- try {
- for (int i = 0; i < values.length; i++) {
- values[i] = Float.parseFloat(strValues[i]);
- }
- } catch (java.lang.NumberFormatException ex) {
- return null;
- }
- return values;
- }
-
- private static float[] multiply(float[] matrix, float[] other) {
- if (matrix == null) {
- return other;
- }
- float[] result = new float[16];
- Matrix.multiplyMM(result, 0, matrix, 0, other, 0);
- return result;
- }
-
- /**
- * Sets the surface flinger's Daltonization mode. This adjusts the color
- * space to correct for or simulate various types of color blindness.
- *
- * @param mode new Daltonization mode
- */
- private static void setDaltonizerMode(int mode) {
- try {
- final IBinder flinger = ServiceManager.getService("SurfaceFlinger");
- if (flinger != null) {
- final Parcel data = Parcel.obtain();
- data.writeInterfaceToken("android.ui.ISurfaceComposer");
- data.writeInt(mode);
- flinger.transact(1014, data, null, 0);
- data.recycle();
- }
- } catch (RemoteException ex) {
- Slog.e(LOG_TAG, "Failed to set Daltonizer mode", ex);
- }
- }
-
- /**
- * Sets the surface flinger's color transformation as a 4x4 matrix. If the
- * matrix is null, color transformations are disabled.
- *
- * @param m the float array that holds the transformation matrix, or null to
- * disable transformation
- */
- private static void setColorTransform(float[] m) {
- try {
- final IBinder flinger = ServiceManager.getService("SurfaceFlinger");
- if (flinger != null) {
- final Parcel data = Parcel.obtain();
- data.writeInterfaceToken("android.ui.ISurfaceComposer");
- if (m != null) {
- data.writeInt(1);
- for (int i = 0; i < 16; i++) {
- data.writeFloat(m[i]);
- }
- } else {
- data.writeInt(0);
- }
- flinger.transact(1015, data, null, 0);
- data.recycle();
- }
- } catch (RemoteException ex) {
- Slog.e(LOG_TAG, "Failed to set color transform", ex);
- }
- }
-
}
diff --git a/services/accessibility/java/com/android/server/accessibility/KeyEventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/KeyEventDispatcher.java
index e03c16e..e8f93b8 100644
--- a/services/accessibility/java/com/android/server/accessibility/KeyEventDispatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/KeyEventDispatcher.java
@@ -121,7 +121,7 @@
Service service = boundServices.get(i);
// Key events are handled only by services that declared
// this capability and requested to filter key events.
- if (!service.mRequestFilterKeyEvents) {
+ if (!service.mRequestFilterKeyEvents || (service.mServiceInterface == null)) {
continue;
}
int filterKeyEventBit = service.mAccessibilityServiceInfo.getCapabilities()
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 2a30229..9d3889b 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -30,6 +30,7 @@
import android.app.admin.DevicePolicyManagerInternal.OnCrossProfileWidgetProvidersChangeListener;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
+import android.appwidget.PendingHostUpdate;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -72,10 +73,12 @@
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.AttributeSet;
+import android.util.LongSparseArray;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.util.SparseLongArray;
import android.util.TypedValue;
import android.util.Xml;
import android.view.Display;
@@ -738,8 +741,8 @@
}
@Override
- public ParceledListSlice<RemoteViews> startListening(IAppWidgetHost callbacks,
- String callingPackage, int hostId, int[] appWidgetIds, int[] updatedIds) {
+ public ParceledListSlice<PendingHostUpdate> startListening(IAppWidgetHost callbacks,
+ String callingPackage, int hostId, int[] appWidgetIds) {
final int userId = UserHandle.getCallingUserId();
if (DEBUG) {
@@ -759,18 +762,19 @@
host.callbacks = callbacks;
int N = appWidgetIds.length;
- ArrayList<RemoteViews> outViews = new ArrayList<>(N);
- RemoteViews rv;
- int added = 0;
+ ArrayList<PendingHostUpdate> outUpdates = new ArrayList<>(N);
+
+ LongSparseArray<PendingHostUpdate> updatesMap = new LongSparseArray<>();
for (int i = 0; i < N; i++) {
- rv = host.getPendingViewsForId(appWidgetIds[i]);
- if (rv != null) {
- updatedIds[added] = appWidgetIds[i];
- outViews.add(rv);
- added++;
+ if (host.getPendingUpdatesForId(appWidgetIds[i], updatesMap)) {
+ // We key the updates based on time, so that the values are sorted by time.
+ int M = updatesMap.size();
+ for (int j = 0; j < M; j++) {
+ outUpdates.add(updatesMap.valueAt(j));
+ }
}
}
- return new ParceledListSlice<>(outViews);
+ return new ParceledListSlice<>(outUpdates);
}
}
@@ -1810,6 +1814,15 @@
}
private void scheduleNotifyAppWidgetViewDataChanged(Widget widget, int viewId) {
+ if (viewId == ID_VIEWS_UPDATE || viewId == ID_PROVIDER_CHANGED) {
+ // A view id should never collide with these constants but a developer can call this
+ // method with a wrong id. In that case, ignore the call.
+ return;
+ }
+ long requestTime = SystemClock.uptimeMillis();
+ if (widget != null) {
+ widget.updateTimes.put(viewId, requestTime);
+ }
if (widget == null || widget.host == null || widget.host.zombie
|| widget.host.callbacks == null || widget.provider == null
|| widget.provider.zombie) {
@@ -1819,6 +1832,7 @@
SomeArgs args = SomeArgs.obtain();
args.arg1 = widget.host;
args.arg2 = widget.host.callbacks;
+ args.arg3 = requestTime;
args.argi1 = widget.appWidgetId;
args.argi2 = viewId;
@@ -1829,9 +1843,10 @@
private void handleNotifyAppWidgetViewDataChanged(Host host, IAppWidgetHost callbacks,
- int appWidgetId, int viewId) {
+ int appWidgetId, int viewId, long requestTime) {
try {
callbacks.viewDataChanged(appWidgetId, viewId);
+ host.lastWidgetUpdateTime = requestTime;
} catch (RemoteException re) {
// It failed; remove the callback. No need to prune because
// we know that this host is still referenced by this instance.
@@ -1880,7 +1895,7 @@
private void scheduleNotifyUpdateAppWidgetLocked(Widget widget, RemoteViews updateViews) {
long requestTime = SystemClock.uptimeMillis();
if (widget != null) {
- widget.lastUpdateTime = requestTime;
+ widget.updateTimes.put(ID_VIEWS_UPDATE, requestTime);
}
if (widget == null || widget.provider == null || widget.provider.zombie
|| widget.host.callbacks == null || widget.host.zombie) {
@@ -1890,7 +1905,7 @@
SomeArgs args = SomeArgs.obtain();
args.arg1 = widget.host;
args.arg2 = widget.host.callbacks;
- args.arg3 = updateViews;
+ args.arg3 = (updateViews != null) ? updateViews.clone() : null;
args.arg4 = requestTime;
args.argi1 = widget.appWidgetId;
@@ -1913,6 +1928,12 @@
}
private void scheduleNotifyProviderChangedLocked(Widget widget) {
+ long requestTime = SystemClock.uptimeMillis();
+ if (widget != null) {
+ // When the provider changes, reset everything else.
+ widget.updateTimes.clear();
+ widget.updateTimes.append(ID_PROVIDER_CHANGED, requestTime);
+ }
if (widget == null || widget.provider == null || widget.provider.zombie
|| widget.host.callbacks == null || widget.host.zombie) {
return;
@@ -1922,6 +1943,7 @@
args.arg1 = widget.host;
args.arg2 = widget.host.callbacks;
args.arg3 = widget.provider.info;
+ args.arg4 = requestTime;
args.argi1 = widget.appWidgetId;
mCallbackHandler.obtainMessage(
@@ -1930,9 +1952,10 @@
}
private void handleNotifyProviderChanged(Host host, IAppWidgetHost callbacks,
- int appWidgetId, AppWidgetProviderInfo info) {
+ int appWidgetId, AppWidgetProviderInfo info, long requestTime) {
try {
callbacks.providerChanged(appWidgetId, info);
+ host.lastWidgetUpdateTime = requestTime;
} catch (RemoteException re) {
synchronized (mLock){
Slog.e(TAG, "Widget host dead: " + host.id, re);
@@ -3416,10 +3439,11 @@
Host host = (Host) args.arg1;
IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
AppWidgetProviderInfo info = (AppWidgetProviderInfo)args.arg3;
+ long requestTime = (Long) args.arg4;
final int appWidgetId = args.argi1;
args.recycle();
- handleNotifyProviderChanged(host, callbacks, appWidgetId, info);
+ handleNotifyProviderChanged(host, callbacks, appWidgetId, info, requestTime);
} break;
case MSG_NOTIFY_PROVIDERS_CHANGED: {
@@ -3435,11 +3459,13 @@
SomeArgs args = (SomeArgs) message.obj;
Host host = (Host) args.arg1;
IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
+ long requestTime = (Long) args.arg3;
final int appWidgetId = args.argi1;
final int viewId = args.argi2;
args.recycle();
- handleNotifyAppWidgetViewDataChanged(host, callbacks, appWidgetId, viewId);
+ handleNotifyAppWidgetViewDataChanged(host, callbacks, appWidgetId, viewId,
+ requestTime);
} break;
}
}
@@ -3778,20 +3804,41 @@
}
/**
- * Returns the RemoveViews for the provided widget id if an update is pending
- * for that widget.
+ * Adds all pending updates in {@param outUpdates} keys by the update time.
*/
- public RemoteViews getPendingViewsForId(int appWidgetId) {
+ public boolean getPendingUpdatesForId(int appWidgetId,
+ LongSparseArray<PendingHostUpdate> outUpdates) {
long updateTime = lastWidgetUpdateTime;
int N = widgets.size();
for (int i = 0; i < N; i++) {
Widget widget = widgets.get(i);
- if (widget.appWidgetId == appWidgetId
- && widget.lastUpdateTime > updateTime) {
- return cloneIfLocalBinder(widget.getEffectiveViewsLocked());
+ if (widget.appWidgetId == appWidgetId) {
+ outUpdates.clear();
+ for (int j = widget.updateTimes.size() - 1; j >= 0; j--) {
+ long time = widget.updateTimes.valueAt(j);
+ if (time <= updateTime) {
+ continue;
+ }
+ int id = widget.updateTimes.keyAt(j);
+ final PendingHostUpdate update;
+ switch (id) {
+ case ID_PROVIDER_CHANGED:
+ update = PendingHostUpdate.providerChanged(
+ appWidgetId, widget.provider.info);
+ break;
+ case ID_VIEWS_UPDATE:
+ update = PendingHostUpdate.updateAppWidget(appWidgetId,
+ cloneIfLocalBinder(widget.getEffectiveViewsLocked()));
+ break;
+ default:
+ update = PendingHostUpdate.viewDataChanged(appWidgetId, id);
+ }
+ outUpdates.put(time, update);
+ }
+ return true;
}
}
- return null;
+ return false;
}
@Override
@@ -3856,6 +3903,10 @@
}
}
+ // These can be any constants that would not collide with a resource id.
+ private static final int ID_VIEWS_UPDATE = 0;
+ private static final int ID_PROVIDER_CHANGED = 1;
+
private static final class Widget {
int appWidgetId;
int restoredId; // tracking & remapping any restored state
@@ -3864,7 +3915,8 @@
RemoteViews maskedViews;
Bundle options;
Host host;
- long lastUpdateTime;
+ // timestamps for various operations
+ SparseLongArray updateTimes = new SparseLongArray(2);
@Override
public String toString() {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index e6f99c1..497eac9 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -234,6 +234,7 @@
private static final int MSG_WIDGET_BROADCAST = 13;
private static final int MSG_RUN_FULL_TRANSPORT_BACKUP = 14;
private static final int MSG_REQUEST_BACKUP = 15;
+ private static final int MSG_SCHEDULE_BACKUP_PACKAGE = 16;
// backup task state machine tick
static final int MSG_BACKUP_RESTORE_STEP = 20;
@@ -796,7 +797,7 @@
queue, oldJournal, null, null, false);
Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt);
sendMessage(pbtMessage);
- } catch (RemoteException e) {
+ } catch (Exception e) {
// unable to ask the transport its dir name -- transient failure, since
// the above check succeeded. Try again next time.
Slog.e(TAG, "Transport became unavailable attempting backup");
@@ -939,7 +940,7 @@
}
if (sets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
} catch (Exception e) {
- Slog.e(TAG, "Error from transport getting set list");
+ Slog.e(TAG, "Error from transport getting set list: " + e.getMessage());
} finally {
if (params.observer != null) {
try {
@@ -947,7 +948,7 @@
} catch (RemoteException re) {
Slog.e(TAG, "Unable to report listing to observer");
} catch (Exception e) {
- Slog.e(TAG, "Restore observer threw", e);
+ Slog.e(TAG, "Restore observer threw: " + e.getMessage());
}
}
@@ -1037,6 +1038,16 @@
sendMessage(pbtMessage);
break;
}
+
+ case MSG_SCHEDULE_BACKUP_PACKAGE:
+ {
+ String pkgName = (String)msg.obj;
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "MSG_SCHEDULE_BACKUP_PACKAGE " + pkgName);
+ }
+ dataChangedImpl(pkgName);
+ break;
+ }
}
}
}
@@ -1216,7 +1227,7 @@
// Now that we know about valid backup participants, parse any
// leftover journal files into the pending backup set
- parseLeftoverJournals();
+ mBackupHandler.post(() -> parseLeftoverJournals());
// Power management
mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
@@ -1759,8 +1770,10 @@
}
return; // done; don't fall through to the error case
}
- } catch (RemoteException e) {
+ } catch (Exception e) {
// transport threw when asked its name; fall through to the lookup-failed case
+ Slog.e(TAG, "Transport " + transportName + " failed to report name: "
+ + e.getMessage());
}
// The named transport doesn't exist or threw. This operation is
@@ -1848,7 +1861,7 @@
System.currentTimeMillis() + delay, mRunInitIntent);
}
}
- } catch (RemoteException e) {
+ } catch (Exception e) {
// the transport threw when asked its file naming prefs; declare it invalid
Slog.e(TAG, "Unable to register transport as " + name);
mTransportNames.remove(component);
@@ -2054,8 +2067,9 @@
IBackupTransport transport = IBackupTransport.Stub.asInterface(service);
registerTransport(transport.name(), name, transport);
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 1);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to register transport " + component);
+ } catch (Exception e) {
+ Slog.e(TAG, "Unable to register transport " + component
+ + ": " + e.getMessage());
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 0);
}
}
@@ -2161,7 +2175,7 @@
int uid = pkg.applicationInfo.uid;
HashSet<String> set = mBackupParticipants.get(uid);
if (set == null) {
- set = new HashSet<String>();
+ set = new HashSet<>();
mBackupParticipants.put(uid, set);
}
set.add(pkg.packageName);
@@ -2169,7 +2183,9 @@
// Schedule a backup for it on general principles
if (MORE_DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName);
- dataChangedImpl(pkg.packageName);
+ Message msg = mBackupHandler
+ .obtainMessage(MSG_SCHEDULE_BACKUP_PACKAGE, pkg.packageName);
+ mBackupHandler.sendMessage(msg);
}
}
}
@@ -2516,8 +2532,8 @@
String dirName;
try {
dirName = transport.transportDirName();
- } catch (RemoteException e) {
- Slog.e(TAG, "Transport became unavailable while attempting backup");
+ } catch (Exception e) {
+ Slog.e(TAG, "Transport unavailable while attempting backup: " + e.getMessage());
sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
return BackupManager.ERROR_TRANSPORT_ABORTED;
}
@@ -2961,9 +2977,10 @@
try {
mCurrentToken = mTransport.getCurrentRestoreSet();
writeRestoreTokens();
- } catch (RemoteException e) {
+ } catch (Exception e) {
// nothing for it at this point, unfortunately, but this will be
// recorded the next time we fully succeed.
+ Slog.e(TAG, "Transport threw reporting restore set: " + e.getMessage());
addBackupTrace("transport threw returning token");
}
}
@@ -2988,7 +3005,7 @@
}
}
} catch (Exception e) {
- Slog.w(TAG, "Failed to query transport name heading for init", e);
+ Slog.w(TAG, "Failed to query transport name for init: " + e.getMessage());
// swallow it and proceed; we don't rely on this
}
clearMetadata();
@@ -3354,8 +3371,8 @@
try {
long quota = mTransport.getBackupQuota(mCurrentPackage.packageName, false);
mAgentBinder.doQuotaExceeded(size, quota);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to contact backup agent for quota exceeded");
+ } catch (Exception e) {
+ Slog.e(TAG, "Unable to notify about quota exceeded: " + e.getMessage());
}
}
nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
@@ -3393,7 +3410,7 @@
try {
delay = mTransport.requestBackupTime();
} catch (Exception e) {
- Slog.w(TAG, "Unable to contact transport for recommended backoff");
+ Slog.w(TAG, "Unable to contact transport for recommended backoff: " + e.getMessage());
delay = 0; // use the scheduler's default
}
KeyValueBackupJob.schedule(mContext, delay);
@@ -4311,7 +4328,10 @@
Slog.e(TAG, "Internal exception during full backup", e);
} finally {
try {
- if (out != null) out.close();
+ if (out != null) {
+ out.flush();
+ out.close();
+ }
mOutputFile.close();
} catch (IOException e) {
/* nothing we can do about this */
@@ -4649,6 +4669,13 @@
}
cleanUpPipes(transportPipes);
cleanUpPipes(enginePipes);
+ if (currentPackage.applicationInfo != null) {
+ Slog.i(TAG, "Unbinding agent in " + packageName);
+ addBackupTrace("unbinding " + packageName);
+ try {
+ mActivityManager.unbindBackupAgent(currentPackage.applicationInfo);
+ } catch (RemoteException e) { /* can't happen; activity manager is local */ }
+ }
}
} catch (Exception e) {
backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
@@ -4984,7 +5011,7 @@
return false;
}
} catch (Exception e) {
- Slog.w(TAG, "Unable to contact transport");
+ Slog.w(TAG, "Unable to get transport name: " + e.getMessage());
return false;
}
@@ -5474,11 +5501,11 @@
// If the policy is satisfied, go ahead and set up to pipe the
// data to the agent.
- if (DEBUG && okay && mAgent != null) {
+ if (MORE_DEBUG && okay && mAgent != null) {
Slog.i(TAG, "Reusing existing agent instance");
}
if (okay && mAgent == null) {
- if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
+ if (MORE_DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
try {
mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
@@ -5680,16 +5707,21 @@
}
void tearDownPipes() {
- if (mPipes != null) {
- try {
- mPipes[0].close();
- mPipes[0] = null;
- mPipes[1].close();
- mPipes[1] = null;
- } catch (IOException e) {
- Slog.w(TAG, "Couldn't close agent pipes", e);
+ // Teardown might arise from the inline restore processing or from the asynchronous
+ // timeout mechanism, and these might race. Make sure we don't try to close and
+ // null out the pipes twice.
+ synchronized (this) {
+ if (mPipes != null) {
+ try {
+ mPipes[0].close();
+ mPipes[0] = null;
+ mPipes[1].close();
+ mPipes[1] = null;
+ } catch (IOException e) {
+ Slog.w(TAG, "Couldn't close agent pipes", e);
+ }
+ mPipes = null;
}
- mPipes = null;
}
}
@@ -8208,9 +8240,9 @@
// Success; cache the metadata and continue as expected with the
// next state already enqueued
- } catch (RemoteException e) {
+ } catch (Exception e) {
// If we lost the transport at any time, halt
- Slog.e(TAG, "Unable to contact transport for restore");
+ Slog.e(TAG, "Unable to contact transport for restore: " + e.getMessage());
mStatus = BackupTransport.TRANSPORT_ERROR;
mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
executeNextState(UnifiedRestoreState.FINAL);
@@ -8307,8 +8339,9 @@
nextState = UnifiedRestoreState.RUNNING_QUEUE;
return;
}
- } catch (RemoteException e) {
- Slog.e(TAG, "Can't get next target from transport; ending restore");
+ } catch (Exception e) {
+ Slog.e(TAG, "Can't get next restore target from transport; halting: "
+ + e.getMessage());
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
nextState = UnifiedRestoreState.FINAL;
return;
@@ -8618,11 +8651,11 @@
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
mCurrentPackage.packageName, "I/O error on pipes");
status = BackupTransport.AGENT_ERROR;
- } catch (RemoteException e) {
- // The transport went away; terminate the whole operation. Closing
+ } catch (Exception e) {
+ // The transport threw; terminate the whole operation. Closing
// the sockets will wake up the engine and it will then tidy up the
// remote end.
- Slog.e(TAG, "Transport failed during restore");
+ Slog.e(TAG, "Transport failed during restore: " + e.getMessage());
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
status = BackupTransport.TRANSPORT_ERROR;
} finally {
@@ -8660,9 +8693,10 @@
// level is immaterial; we need to tell the transport to bail
try {
mTransport.abortFullRestore();
- } catch (RemoteException e) {
+ } catch (Exception e) {
// transport itself is dead; make sure we handle this as a
// fatal error
+ Slog.e(TAG, "Transport threw from abortFullRestore: " + e.getMessage());
status = BackupTransport.TRANSPORT_ERROR;
}
@@ -9010,16 +9044,15 @@
// Tell the transport to remove all the persistent storage for the app
// TODO - need to handle failures
mTransport.clearBackupData(mPackage);
- } catch (RemoteException e) {
- // can't happen; the transport is local
} catch (Exception e) {
- Slog.e(TAG, "Transport threw attempting to clear data for " + mPackage);
+ Slog.e(TAG, "Transport threw clearing data for " + mPackage + ": " + e.getMessage());
} finally {
try {
// TODO - need to handle failures
mTransport.finishBackup();
- } catch (RemoteException e) {
- // can't happen; the transport is local
+ } catch (Exception e) {
+ // Nothing we can do here, alas
+ Slog.e(TAG, "Unable to mark clear operation finished: " + e.getMessage());
}
// Last but not least, release the cpu
@@ -9078,8 +9111,6 @@
System.currentTimeMillis() + delay, mRunInitIntent);
}
}
- } catch (RemoteException e) {
- // can't happen; the transports are local
} catch (Exception e) {
Slog.e(TAG, "Unexpected error performing init", e);
} finally {
@@ -9767,8 +9798,9 @@
if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
+ intent);
return intent;
- } catch (RemoteException e) {
+ } catch (Exception e) {
/* fall through to return null */
+ Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
}
}
}
@@ -9792,8 +9824,9 @@
final String text = transport.currentDestinationString();
if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text);
return text;
- } catch (RemoteException e) {
+ } catch (Exception e) {
/* fall through to return null */
+ Slog.e(TAG, "Unable to get string from transport: " + e.getMessage());
}
}
}
@@ -9814,8 +9847,9 @@
if (MORE_DEBUG) Slog.d(TAG, "getDataManagementIntent() returning intent "
+ intent);
return intent;
- } catch (RemoteException e) {
+ } catch (Exception e) {
/* fall through to return null */
+ Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
}
}
}
@@ -9836,8 +9870,9 @@
final String text = transport.dataManagementLabel();
if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text);
return text;
- } catch (RemoteException e) {
+ } catch (Exception e) {
/* fall through to return null */
+ Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
}
}
}
@@ -9930,9 +9965,9 @@
msg.obj = new RestoreParams(transport, dirName, null,
restoreSet, packageName, token);
mBackupHandler.sendMessage(msg);
- } catch (RemoteException e) {
- // Binding to the transport broke; back off and proceed with the installation.
- Slog.e(TAG, "Unable to contact transport");
+ } catch (Exception e) {
+ // Calling into the transport broke; back off and proceed with the installation.
+ Slog.e(TAG, "Unable to contact transport: " + e.getMessage());
skip = true;
}
}
@@ -10053,8 +10088,8 @@
try {
return transport.isAppEligibleForBackup(packageInfo,
appGetsFullBackup(packageInfo));
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to contact transport");
+ } catch (Exception e) {
+ Slog.e(TAG, "Unable to ask about eligibility: " + e.getMessage());
}
}
// If transport is not present we couldn't tell that the package is not eligible.
@@ -10156,9 +10191,9 @@
String dirName;
try {
dirName = mRestoreTransport.transportDirName();
- } catch (RemoteException e) {
+ } catch (Exception e) {
// Transport went AWOL; fail.
- Slog.e(TAG, "Unable to contact transport for restore");
+ Slog.e(TAG, "Unable to get transport dir for restore: " + e.getMessage());
return -1;
}
@@ -10238,9 +10273,9 @@
String dirName;
try {
dirName = mRestoreTransport.transportDirName();
- } catch (RemoteException e) {
+ } catch (Exception e) {
// Transport went AWOL; fail.
- Slog.e(TAG, "Unable to contact transport for restore");
+ Slog.e(TAG, "Unable to get transport name for restoreSome: " + e.getMessage());
return -1;
}
@@ -10328,9 +10363,9 @@
String dirName;
try {
dirName = mRestoreTransport.transportDirName();
- } catch (RemoteException e) {
+ } catch (Exception e) {
// Transport went AWOL; fail.
- Slog.e(TAG, "Unable to contact transport for restore");
+ Slog.e(TAG, "Unable to get transport dir for restorePackage: " + e.getMessage());
return -1;
}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 3f59b02..20cca16 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -2472,8 +2472,10 @@
Slog.v(TAG, "Time changed notification from kernel; rebatching");
}
removeImpl(mTimeTickSender);
+ removeImpl(mDateChangeSender);
rebatchAllAlarms();
mClockReceiver.scheduleTimeTickEvent();
+ mClockReceiver.scheduleDateChangedEvent();
synchronized (mLock) {
mNumTimeChanged++;
mLastTimeChangeClockTime = nowRTC;
@@ -2552,7 +2554,9 @@
} else {
// Just in case -- even though no wakeup flag was set, make sure
// we have updated the kernel to the next alarm time.
- rescheduleKernelAlarmsLocked();
+ synchronized (mLock) {
+ rescheduleKernelAlarmsLocked();
+ }
}
}
}
@@ -2695,7 +2699,7 @@
public void scheduleDateChangedEvent() {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
- calendar.set(Calendar.HOUR, 0);
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
diff --git a/services/core/java/com/android/server/AnyMotionDetector.java b/services/core/java/com/android/server/AnyMotionDetector.java
index a8ae914d..f93c716 100644
--- a/services/core/java/com/android/server/AnyMotionDetector.java
+++ b/services/core/java/com/android/server/AnyMotionDetector.java
@@ -70,6 +70,9 @@
/** The interval between accelerometer orientation measurements. */
private static final long ORIENTATION_MEASUREMENT_INTERVAL_MILLIS = 5000;
+ /** The maximum duration we will hold a wakelock to determine stationary status. */
+ private static final long WAKELOCK_TIMEOUT_MILLIS = 30000;
+
/**
* The duration in milliseconds after which an orientation measurement is considered
* too stale to be used.
@@ -141,25 +144,30 @@
mCurrentGravityVector = null;
mPreviousGravityVector = null;
mWakeLock.acquire();
+ Message wakelockTimeoutMsg = Message.obtain(mHandler, mWakelockTimeout);
+ mHandler.sendMessageDelayed(wakelockTimeoutMsg, WAKELOCK_TIMEOUT_MILLIS);
startOrientationMeasurementLocked();
}
}
}
public void stop() {
- if (mState == STATE_ACTIVE) {
- synchronized (mLock) {
+ synchronized (mLock) {
+ if (mState == STATE_ACTIVE) {
mState = STATE_INACTIVE;
if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE.");
- if (mMeasurementInProgress) {
- mMeasurementInProgress = false;
- mSensorManager.unregisterListener(mListener);
- }
- mHandler.removeCallbacks(mMeasurementTimeout);
- mHandler.removeCallbacks(mSensorRestart);
- mCurrentGravityVector = null;
- mPreviousGravityVector = null;
+ }
+ if (mMeasurementInProgress) {
+ mMeasurementInProgress = false;
+ mSensorManager.unregisterListener(mListener);
+ }
+ mHandler.removeCallbacks(mMeasurementTimeout);
+ mHandler.removeCallbacks(mSensorRestart);
+ mCurrentGravityVector = null;
+ mPreviousGravityVector = null;
+ if (mWakeLock.isHeld()) {
mWakeLock.release();
+ mHandler.removeCallbacks(mWakelockTimeout);
}
}
}
@@ -173,9 +181,8 @@
mMeasurementInProgress = true;
mRunningStats.reset();
}
- Message msg = Message.obtain(mHandler, mMeasurementTimeout);
- msg.setAsynchronous(true);
- mHandler.sendMessageDelayed(msg, ACCELEROMETER_DATA_TIMEOUT_MILLIS);
+ Message measurementTimeoutMsg = Message.obtain(mHandler, mMeasurementTimeout);
+ mHandler.sendMessageDelayed(measurementTimeoutMsg, ACCELEROMETER_DATA_TIMEOUT_MILLIS);
}
}
@@ -186,10 +193,12 @@
if (mMeasurementInProgress) {
mSensorManager.unregisterListener(mListener);
mHandler.removeCallbacks(mMeasurementTimeout);
- long detectionEndTime = SystemClock.elapsedRealtime();
mMeasurementInProgress = false;
mPreviousGravityVector = mCurrentGravityVector;
mCurrentGravityVector = mRunningStats.getRunningAverage();
+ if (mRunningStats.getSampleCount() == 0) {
+ Slog.w(TAG, "No accelerometer data acquired for orientation measurement.");
+ }
if (DEBUG) {
Slog.d(TAG, "mRunningStats = " + mRunningStats.toString());
String currentGravityVectorString = (mCurrentGravityVector == null) ?
@@ -203,7 +212,10 @@
status = getStationaryStatus();
if (DEBUG) Slog.d(TAG, "getStationaryStatus() returned " + status);
if (status != RESULT_UNKNOWN) {
- mWakeLock.release();
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ mHandler.removeCallbacks(mWakelockTimeout);
+ }
if (DEBUG) {
Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE. status = " + status);
}
@@ -217,7 +229,6 @@
" scheduled in " + ORIENTATION_MEASUREMENT_INTERVAL_MILLIS +
" milliseconds.");
Message msg = Message.obtain(mHandler, mSensorRestart);
- msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg, ORIENTATION_MEASUREMENT_INTERVAL_MILLIS);
}
}
@@ -271,6 +282,7 @@
}
}
if (status != RESULT_UNKNOWN) {
+ mHandler.removeCallbacks(mWakelockTimeout);
mCallback.onAnyMotionResult(status);
}
}
@@ -290,20 +302,30 @@
};
private final Runnable mMeasurementTimeout = new Runnable() {
- @Override
- public void run() {
- int status = RESULT_UNKNOWN;
- synchronized (mLock) {
- if (DEBUG) Slog.i(TAG, "mMeasurementTimeout. Failed to collect sufficient accel " +
+ @Override
+ public void run() {
+ int status = RESULT_UNKNOWN;
+ synchronized (mLock) {
+ if (DEBUG) Slog.i(TAG, "mMeasurementTimeout. Failed to collect sufficient accel " +
"data within " + ACCELEROMETER_DATA_TIMEOUT_MILLIS + " ms. Stopping " +
"orientation measurement.");
- status = stopOrientationMeasurementLocked();
- }
- if (status != RESULT_UNKNOWN) {
- mCallback.onAnyMotionResult(status);
- }
- }
- };
+ status = stopOrientationMeasurementLocked();
+ }
+ if (status != RESULT_UNKNOWN) {
+ mHandler.removeCallbacks(mWakelockTimeout);
+ mCallback.onAnyMotionResult(status);
+ }
+ }
+ };
+
+ private final Runnable mWakelockTimeout = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mLock) {
+ stop();
+ }
+ }
+ };
/**
* A timestamped three dimensional vector and some vector operations.
diff --git a/services/core/java/com/android/server/AttributeCache.java b/services/core/java/com/android/server/AttributeCache.java
index 57f18c0..58ec836 100644
--- a/services/core/java/com/android/server/AttributeCache.java
+++ b/services/core/java/com/android/server/AttributeCache.java
@@ -25,23 +25,24 @@
import android.content.res.TypedArray;
import android.os.UserHandle;
import android.util.ArrayMap;
+import android.util.LruCache;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-import java.lang.ref.WeakReference;
-
/**
* TODO: This should be better integrated into the system so it doesn't need
* special calls from the activity manager to clear it.
*/
public final class AttributeCache {
+ private static final int CACHE_SIZE = 4;
private static AttributeCache sInstance = null;
private final Context mContext;
@GuardedBy("this")
- private final ArrayMap<String, WeakReference<Package>> mPackages = new ArrayMap<>();
+ private final LruCache<String, Package> mPackages = new LruCache<>(CACHE_SIZE);
+
@GuardedBy("this")
private final Configuration mConfiguration = new Configuration();
@@ -86,15 +87,12 @@
public void removePackage(String packageName) {
synchronized (this) {
- final WeakReference<Package> ref = mPackages.remove(packageName);
- final Package pkg = (ref != null) ? ref.get() : null;
+ final Package pkg = mPackages.remove(packageName);
if (pkg != null) {
- if (pkg.mMap != null) {
- for (int i = 0; i < pkg.mMap.size(); i++) {
- final ArrayMap<int[], Entry> map = pkg.mMap.valueAt(i);
- for (int j = 0; j < map.size(); j++) {
- map.valueAt(j).recycle();
- }
+ for (int i = 0; i < pkg.mMap.size(); i++) {
+ final ArrayMap<int[], Entry> map = pkg.mMap.valueAt(i);
+ for (int j = 0; j < map.size(); j++) {
+ map.valueAt(j).recycle();
}
}
@@ -113,15 +111,14 @@
// The configurations being masked out are ones that commonly
// change so we don't want flushing the cache... all others
// will flush the cache.
- mPackages.clear();
+ mPackages.evictAll();
}
}
}
public Entry get(String packageName, int resId, int[] styleable, int userId) {
synchronized (this) {
- WeakReference<Package> ref = mPackages.get(packageName);
- Package pkg = (ref != null) ? ref.get() : null;
+ Package pkg = mPackages.get(packageName);
ArrayMap<int[], Entry> map = null;
Entry ent = null;
if (pkg != null) {
@@ -144,7 +141,7 @@
return null;
}
pkg = new Package(context);
- mPackages.put(packageName, new WeakReference<>(pkg));
+ mPackages.put(packageName, pkg);
}
if (map == null) {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 172025b..8c5887f 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -425,6 +425,24 @@
return false;
}
+ public int getState() {
+ if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
+ (!checkIfCallerIsForegroundUser())) {
+ Slog.w(TAG, "getState(): not allowed for non-active and non system user");
+ return BluetoothAdapter.STATE_OFF;
+ }
+
+ try {
+ mBluetoothLock.readLock().lock();
+ if (mBluetooth != null) return mBluetooth.getState();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "getState()", e);
+ } finally {
+ mBluetoothLock.readLock().unlock();
+ }
+ return BluetoothAdapter.STATE_OFF;
+ }
+
class ClientDeathRecipient implements IBinder.DeathRecipient {
public void binderDied() {
if (DBG) Slog.d(TAG, "Binder is dead - unregister Ble App");
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 534b544..14243c5 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -38,7 +38,6 @@
import android.annotation.Nullable;
import android.app.BroadcastOptions;
-import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -76,6 +75,7 @@
import android.net.UidRange;
import android.net.Uri;
import android.net.metrics.DefaultNetworkEvent;
+import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
import android.os.Binder;
import android.os.Build;
@@ -93,6 +93,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -124,14 +125,18 @@
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.MessageUtils;
+import com.android.internal.util.WakeupMessage;
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.DataConnectionStats;
import com.android.server.connectivity.KeepaliveTracker;
import com.android.server.connectivity.Nat464Xlat;
+import com.android.server.connectivity.LingerMonitor;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkDiagnostics;
import com.android.server.connectivity.NetworkMonitor;
+import com.android.server.connectivity.NetworkNotificationManager;
+import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
import com.android.server.connectivity.PacManager;
import com.android.server.connectivity.PermissionMonitor;
import com.android.server.connectivity.Tethering;
@@ -170,7 +175,7 @@
*/
public class ConnectivityService extends IConnectivityManager.Stub
implements PendingIntent.OnFinished {
- private static final String TAG = "ConnectivityService";
+ private static final String TAG = ConnectivityService.class.getSimpleName();
private static final boolean DBG = true;
private static final boolean VDBG = false;
@@ -190,6 +195,12 @@
// connect anyway?" dialog after the user selects a network that doesn't validate.
private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000;
+ // Default to 30s linger time-out. Modifiable only for testing.
+ private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
+ private static final int DEFAULT_LINGER_DELAY_MS = 30_000;
+ @VisibleForTesting
+ protected int mLingerDelayMs; // Can't be final, or test subclass constructors can't change it.
+
// How long to delay to removal of a pending intent based request.
// See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
private final int mReleasePendingIntentDelayMs;
@@ -238,7 +249,8 @@
private static final int DISABLED = 0;
private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(
- new Class[] { AsyncChannel.class, ConnectivityService.class, NetworkAgent.class });
+ new Class[] { AsyncChannel.class, ConnectivityService.class, NetworkAgent.class,
+ NetworkAgentInfo.class });
private enum ReapUnvalidatedNetworks {
// Tear down networks that have no chance (e.g. even if validated) of becoming
@@ -293,7 +305,7 @@
/**
* indicates a timeout period is over - check if we had a network yet or not
- * and if not, call the timeout calback (but leave the request live until they
+ * and if not, call the timeout callback (but leave the request live until they
* cancel it.
* includes a NetworkRequestInfo
*/
@@ -370,6 +382,16 @@
*/
private static final int EVENT_REGISTER_NETWORK_LISTENER_WITH_INTENT = 31;
+ /**
+ * Indicates a caller has requested to have its callback invoked with
+ * the latest LinkProperties or NetworkCapabilities.
+ *
+ * arg1 = UID of caller
+ * obj = NetworkRequest
+ */
+ private static final int EVENT_REQUEST_LINKPROPERTIES = 32;
+ private static final int EVENT_REQUEST_NETCAPABILITIES = 33;
+
/** Handler thread used for both of the handlers below. */
@VisibleForTesting
protected final HandlerThread mHandlerThread;
@@ -416,6 +438,8 @@
TelephonyManager mTelephonyManager;
private KeepaliveTracker mKeepaliveTracker;
+ private NetworkNotificationManager mNotifier;
+ private LingerMonitor mLingerMonitor;
// sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
private final static int MIN_NET_ID = 100; // some reserved marks
@@ -429,6 +453,11 @@
private static final int MAX_NETWORK_REQUEST_LOGS = 20;
private final LocalLog mNetworkRequestInfoLogs = new LocalLog(MAX_NETWORK_REQUEST_LOGS);
+ // NetworkInfo blocked and unblocked String log entries
+ // TODO: consider reducing memory usage. Each log line is ~40 2B chars, for a total of ~8kB.
+ private static final int MAX_NETWORK_INFO_LOGS = 100;
+ private final LocalLog mNetworkInfoBlockingLogs = new LocalLog(MAX_NETWORK_INFO_LOGS);
+
// Array of <Network,ReadOnlyLocalLogs> tracking network validation and results
private static final int MAX_VALIDATION_LOGS = 10;
private static class ValidationLog {
@@ -454,6 +483,8 @@
}
}
+ private final IpConnectivityLog mMetricsLog;
+
/**
* Implements support for the legacy "one network per network type" model.
*
@@ -487,8 +518,16 @@
*
* The actual lists are populated when we scan the network types that
* are supported on this device.
+ *
+ * Threading model:
+ * - addSupportedType() is only called in the constructor
+ * - add(), update(), remove() are only called from the ConnectivityService handler thread.
+ * They are therefore not thread-safe with respect to each other.
+ * - getNetworkForType() can be called at any time on binder threads. It is synchronized
+ * on mTypeLists to be thread-safe with respect to a concurrent remove call.
+ * - dump is thread-safe with respect to concurrent add and remove calls.
*/
- private ArrayList<NetworkAgentInfo> mTypeLists[];
+ private final ArrayList<NetworkAgentInfo> mTypeLists[];
public LegacyTypeTracker() {
mTypeLists = (ArrayList<NetworkAgentInfo>[])
@@ -508,11 +547,12 @@
}
public NetworkAgentInfo getNetworkForType(int type) {
- if (isTypeSupported(type) && !mTypeLists[type].isEmpty()) {
- return mTypeLists[type].get(0);
- } else {
- return null;
+ synchronized (mTypeLists) {
+ if (isTypeSupported(type) && !mTypeLists[type].isEmpty()) {
+ return mTypeLists[type].get(0);
+ }
}
+ return null;
}
private void maybeLogBroadcast(NetworkAgentInfo nai, DetailedState state, int type,
@@ -535,12 +575,13 @@
if (list.contains(nai)) {
return;
}
-
- list.add(nai);
+ synchronized (mTypeLists) {
+ list.add(nai);
+ }
// Send a broadcast if this is the first network of its type or if it's the default.
final boolean isDefaultNetwork = isDefaultNetwork(nai);
- if (list.size() == 1 || isDefaultNetwork) {
+ if ((list.size() == 1) || isDefaultNetwork) {
maybeLogBroadcast(nai, DetailedState.CONNECTED, type, isDefaultNetwork);
sendLegacyNetworkBroadcast(nai, DetailedState.CONNECTED, type);
}
@@ -552,11 +593,12 @@
if (list == null || list.isEmpty()) {
return;
}
-
final boolean wasFirstNetwork = list.get(0).equals(nai);
- if (!list.remove(nai)) {
- return;
+ synchronized (mTypeLists) {
+ if (!list.remove(nai)) {
+ return;
+ }
}
final DetailedState state = DetailedState.DISCONNECTED;
@@ -591,8 +633,8 @@
for (int type = 0; type < mTypeLists.length; type++) {
final ArrayList<NetworkAgentInfo> list = mTypeLists[type];
final boolean contains = (list != null && list.contains(nai));
- final boolean isFirst = (list != null && list.size() > 0 && nai == list.get(0));
- if (isFirst || (contains && isDefault)) {
+ final boolean isFirst = contains && (nai == list.get(0));
+ if (isFirst || contains && isDefault) {
maybeLogBroadcast(nai, state, type, isDefault);
sendLegacyNetworkBroadcast(nai, state, type);
}
@@ -617,10 +659,12 @@
pw.println();
pw.println("Current state:");
pw.increaseIndent();
- for (int type = 0; type < mTypeLists.length; type++) {
- if (mTypeLists[type] == null|| mTypeLists[type].size() == 0) continue;
- for (NetworkAgentInfo nai : mTypeLists[type]) {
- pw.println(type + " " + naiToString(nai));
+ synchronized (mTypeLists) {
+ for (int type = 0; type < mTypeLists.length; type++) {
+ if (mTypeLists[type] == null || mTypeLists[type].isEmpty()) continue;
+ for (NetworkAgentInfo nai : mTypeLists[type]) {
+ pw.println(type + " " + naiToString(nai));
+ }
}
}
pw.decreaseIndent();
@@ -637,11 +681,18 @@
public ConnectivityService(Context context, INetworkManagementService netManager,
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
+ this(context, netManager, statsService, policyManager, new IpConnectivityLog());
+ }
+
+ @VisibleForTesting
+ protected ConnectivityService(Context context, INetworkManagementService netManager,
+ INetworkStatsService statsService, INetworkPolicyManager policyManager,
+ IpConnectivityLog logger) {
if (DBG) log("ConnectivityService starting up");
+ mMetricsLog = logger;
mDefaultRequest = createInternetRequestForTransport(-1);
- NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest,
- new Binder(), NetworkRequestType.REQUEST);
+ NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest, new Binder());
mNetworkRequests.put(mDefaultRequest, defaultNRI);
mNetworkRequestInfoLogs.log("REGISTER " + defaultNRI);
@@ -666,6 +717,8 @@
mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
+ mLingerDelayMs = SystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
+
mContext = checkNotNull(context, "missing Context");
mNetd = checkNotNull(netManager, "missing INetworkManagementService");
mStatsService = checkNotNull(statsService, "missing INetworkStatsService");
@@ -783,6 +836,9 @@
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mKeepaliveTracker = new KeepaliveTracker(mHandler);
+ mNotifier = new NetworkNotificationManager(mContext, mTelephonyManager,
+ mContext.getSystemService(NotificationManager.class));
+ mLingerMonitor = new LingerMonitor(mContext, mNotifier);
}
private NetworkRequest createInternetRequestForTransport(int transportType) {
@@ -792,7 +848,19 @@
if (transportType > -1) {
netCap.addTransportType(transportType);
}
- return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId());
+ return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId(),
+ NetworkRequest.Type.REQUEST);
+ }
+
+ // Used only for testing.
+ // TODO: Delete this and either:
+ // 1. Give Fake SettingsProvider the ability to send settings change notifications (requires
+ // changing ContentResolver to make registerContentObserver non-final).
+ // 2. Give FakeSettingsProvider an alternative notification mechanism and have the test use it
+ // by subclassing SettingsObserver.
+ @VisibleForTesting
+ void updateMobileDataAlwaysOn() {
+ mHandler.sendEmptyMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON);
}
private void handleMobileDataAlwaysOn() {
@@ -805,7 +873,7 @@
if (enable) {
handleRegisterNetworkRequest(new NetworkRequestInfo(
- null, mDefaultMobileDataRequest, new Binder(), NetworkRequestType.REQUEST));
+ null, mDefaultMobileDataRequest, new Binder()));
} else {
handleReleaseNetworkRequest(mDefaultMobileDataRequest, Process.SYSTEM_UID);
}
@@ -965,7 +1033,9 @@
}
private void maybeLogBlockedNetworkInfo(NetworkInfo ni, int uid) {
- if (ni == null || !LOGD_BLOCKED_NETWORKINFO) return;
+ if (ni == null || !LOGD_BLOCKED_NETWORKINFO) {
+ return;
+ }
boolean removed = false;
boolean added = false;
synchronized (mBlockedAppUids) {
@@ -975,8 +1045,13 @@
removed = true;
}
}
- if (added) log("Returning blocked NetworkInfo to uid=" + uid);
- else if (removed) log("Returning unblocked NetworkInfo to uid=" + uid);
+ if (added) {
+ log("Returning blocked NetworkInfo to uid=" + uid);
+ mNetworkInfoBlockingLogs.log("BLOCKED " + uid);
+ } else if (removed) {
+ log("Returning unblocked NetworkInfo to uid=" + uid);
+ mNetworkInfoBlockingLogs.log("UNBLOCKED " + uid);
+ }
}
/**
@@ -1056,6 +1131,7 @@
return nai != null ? nai.network : null;
}
+ // Public because it's used by mLockdownTracker.
public NetworkInfo getActiveNetworkInfoUnfiltered() {
enforceAccessPermission();
final int uid = Binder.getCallingUid();
@@ -1311,6 +1387,7 @@
* desired
* @return {@code true} on success, {@code false} on failure
*/
+ @Override
public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) {
enforceChangePermission();
if (mProtectedNetworks.contains(networkType)) {
@@ -1515,10 +1592,21 @@
"ConnectivityService");
}
+ private void enforceConnectivityRestrictedNetworksPermission() {
+ try {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS,
+ "ConnectivityService");
+ return;
+ } catch (SecurityException e) { /* fallback to ConnectivityInternalPermission */ }
+ enforceConnectivityInternalPermission();
+ }
+
private void enforceKeepalivePermission() {
mContext.enforceCallingOrSelfPermission(KeepaliveTracker.PERMISSION, "ConnectivityService");
}
+ // Public because it's used by mLockdownTracker.
public void sendConnectedBroadcast(NetworkInfo info) {
enforceConnectivityInternalPermission();
sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
@@ -1875,15 +1963,16 @@
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
pw.println(nai.toString());
pw.increaseIndent();
- pw.println("Requests:");
+ pw.println(String.format("Requests: %d request/%d total",
+ nai.numRequestNetworkRequests(), nai.numNetworkRequests()));
pw.increaseIndent();
- for (int i = 0; i < nai.networkRequests.size(); i++) {
- pw.println(nai.networkRequests.valueAt(i).toString());
+ for (int i = 0; i < nai.numNetworkRequests(); i++) {
+ pw.println(nai.requestAt(i).toString());
}
pw.decreaseIndent();
pw.println("Lingered:");
pw.increaseIndent();
- for (NetworkRequest nr : nai.networkLingered) pw.println(nr.toString());
+ nai.dumpLingerTimers(pw);
pw.decreaseIndent();
pw.decreaseIndent();
}
@@ -1972,6 +2061,12 @@
pw.increaseIndent();
mNetworkRequestInfoLogs.reverseDump(fd, pw, args);
pw.decreaseIndent();
+
+ pw.println();
+ pw.println("mNetworkInfoBlockingLogs (most recent first):");
+ pw.increaseIndent();
+ mNetworkInfoBlockingLogs.reverseDump(fd, pw, args);
+ pw.decreaseIndent();
}
}
@@ -1987,10 +2082,6 @@
return false;
}
- private boolean isRequest(NetworkRequest request) {
- return mNetworkRequests.get(request).isRequest();
- }
-
// must be stateless - things change under us.
private class NetworkStateTrackerHandler extends Handler {
public NetworkStateTrackerHandler(Looper looper) {
@@ -2132,13 +2223,6 @@
}
break;
}
- case NetworkMonitor.EVENT_NETWORK_LINGER_COMPLETE: {
- NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
- if (isLiveNetworkAgent(nai, msg.what)) {
- handleLingerComplete(nai);
- }
- break;
- }
case NetworkMonitor.EVENT_PROVISIONING_NOTIFICATION: {
final int netId = msg.arg2;
final boolean visible = (msg.arg1 != 0);
@@ -2153,15 +2237,31 @@
updateCapabilities(nai, nai.networkCapabilities);
}
if (!visible) {
- setProvNotificationVisibleIntent(false, netId, null, 0, null, null, false);
+ mNotifier.clearNotification(netId);
} else {
if (nai == null) {
loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
break;
}
- setProvNotificationVisibleIntent(true, netId, NotificationType.SIGN_IN,
- nai.networkInfo.getType(), nai.networkInfo.getExtraInfo(),
- (PendingIntent)msg.obj, nai.networkMisc.explicitlySelected);
+ if (!nai.networkMisc.provisioningNotificationDisabled) {
+ mNotifier.showNotification(netId, NotificationType.SIGN_IN, nai, null,
+ (PendingIntent) msg.obj, nai.networkMisc.explicitlySelected);
+ }
+ }
+ break;
+ }
+ }
+ return true;
+ }
+
+ private boolean maybeHandleNetworkAgentInfoMessage(Message msg) {
+ switch (msg.what) {
+ default:
+ return false;
+ case NetworkAgentInfo.EVENT_NETWORK_LINGER_COMPLETE: {
+ NetworkAgentInfo nai = (NetworkAgentInfo) msg.obj;
+ if (nai != null && isLiveNetworkAgent(nai, msg.what)) {
+ handleLingerComplete(nai);
}
break;
}
@@ -2171,31 +2271,33 @@
@Override
public void handleMessage(Message msg) {
- if (!maybeHandleAsyncChannelMessage(msg) && !maybeHandleNetworkMonitorMessage(msg)) {
+ if (!maybeHandleAsyncChannelMessage(msg) &&
+ !maybeHandleNetworkMonitorMessage(msg) &&
+ !maybeHandleNetworkAgentInfoMessage(msg)) {
maybeHandleNetworkAgentMessage(msg);
}
}
}
- private void linger(NetworkAgentInfo nai) {
- nai.lingering = true;
- NetworkEvent.logEvent(nai.network.netId, NetworkEvent.NETWORK_LINGER);
- nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_LINGER);
- notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING);
- }
-
- // Cancel any lingering so the linger timeout doesn't teardown a network.
- // This should be called when a network begins satisfying a NetworkRequest.
- // Note: depending on what state the NetworkMonitor is in (e.g.,
- // if it's awaiting captive portal login, or if validation failed), this
- // may trigger a re-evaluation of the network.
- private void unlinger(NetworkAgentInfo nai) {
- nai.networkLingered.clear();
- if (!nai.lingering) return;
- nai.lingering = false;
- NetworkEvent.logEvent(nai.network.netId, NetworkEvent.NETWORK_UNLINGER);
- if (VDBG) log("Canceling linger of " + nai.name());
- nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
+ private void updateLingerState(NetworkAgentInfo nai, long now) {
+ // 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
+ // 2. If the network was lingering and there are now requests, unlinger it.
+ // 3. If this network is unneeded (which implies it is not lingering), and there is at least
+ // one lingered request, start lingering.
+ nai.updateLingerTimer();
+ if (nai.isLingering() && nai.numRequestNetworkRequests() > 0) {
+ if (DBG) log("Unlingering " + nai.name());
+ nai.unlinger();
+ logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
+ } else if (unneeded(nai) && nai.getLingerExpiry() > 0) { // unneeded() calls isLingering()
+ int lingerTime = (int) (nai.getLingerExpiry() - now);
+ if (DBG) {
+ Log.d(TAG, "Lingering " + nai.name() + " for " + lingerTime + "ms");
+ }
+ nai.linger();
+ logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
+ notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
+ }
}
private void handleAsyncChannelHalfConnect(Message msg) {
@@ -2205,7 +2307,7 @@
if (VDBG) log("NetworkFactory connected");
// A network factory has connected. Send it all current NetworkRequests.
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- if (!nri.isRequest()) continue;
+ if (nri.request.isListen()) continue;
NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK,
(nai != null ? nai.getCurrentScore() : 0), 0, nri.request);
@@ -2240,7 +2342,7 @@
NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
if (nai != null) {
if (DBG) {
- log(nai.name() + " got DISCONNECTED, was satisfying " + nai.networkRequests.size());
+ log(nai.name() + " got DISCONNECTED, was satisfying " + nai.numNetworkRequests());
}
// A network agent has disconnected.
// TODO - if we move the logic to the network agent (have them disconnect
@@ -2277,21 +2379,23 @@
mNetworkForNetId.remove(nai.network.netId);
}
// Remove all previously satisfied requests.
- for (int i = 0; i < nai.networkRequests.size(); i++) {
- NetworkRequest request = nai.networkRequests.valueAt(i);
+ for (int i = 0; i < nai.numNetworkRequests(); i++) {
+ NetworkRequest request = nai.requestAt(i);
NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(request.requestId);
if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) {
mNetworkForRequestId.remove(request.requestId);
sendUpdatedScoreToFactories(request, 0);
}
}
- if (nai.networkRequests.get(mDefaultRequest.requestId) != null) {
+ nai.clearLingerState();
+ if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
removeDataActivityTracking(nai);
notifyLockdownVpn(nai);
requestNetworkTransitionWakelock(nai.name());
}
mLegacyTypeTracker.remove(nai, wasDefault);
rematchAllNetworksAndRequests(null, 0);
+ mLingerMonitor.noteDisconnect(nai);
if (nai.created) {
// Tell netd to clean up the configuration for this network
// (routing rules, DNS, etc).
@@ -2346,7 +2450,7 @@
private void handleRegisterNetworkRequest(NetworkRequestInfo nri) {
mNetworkRequests.put(nri.request, nri);
mNetworkRequestInfoLogs.log("REGISTER " + nri);
- if (!nri.isRequest()) {
+ if (nri.request.isListen()) {
for (NetworkAgentInfo network : mNetworkAgentInfos.values()) {
if (nri.request.networkCapabilities.hasSignalStrength() &&
network.satisfiesImmutableCapabilitiesOf(nri.request)) {
@@ -2355,7 +2459,7 @@
}
}
rematchAllNetworksAndRequests(null, 0);
- if (nri.isRequest() && mNetworkForRequestId.get(nri.request.requestId) == null) {
+ if (nri.request.isRequest() && mNetworkForRequestId.get(nri.request.requestId) == null) {
sendUpdatedScoreToFactories(nri.request, 0);
}
}
@@ -2372,12 +2476,15 @@
// This is whether it is satisfying any NetworkRequests or were it to become validated,
// would it have a chance of satisfying any NetworkRequests.
private boolean unneeded(NetworkAgentInfo nai) {
- if (!nai.everConnected || nai.isVPN() || nai.lingering) return false;
+ if (!nai.everConnected || nai.isVPN() ||
+ nai.isLingering() || nai.numRequestNetworkRequests() > 0) {
+ return false;
+ }
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
// If this Network is already the highest scoring Network for a request, or if
// there is hope for it to become one if it validated, then it is needed.
- if (nri.isRequest() && nai.satisfies(nri.request) &&
- (nai.networkRequests.get(nri.request.requestId) != null ||
+ if (nri.request.isRequest() && nai.satisfies(nri.request) &&
+ (nai.isSatisfyingRequest(nri.request.requestId) ||
// Note that this catches two important cases:
// 1. Unvalidated cellular will not be reaped when unvalidated WiFi
// is currently satisfying the request. This is desirable when
@@ -2393,102 +2500,149 @@
return true;
}
- private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid) {
- NetworkRequestInfo nri = mNetworkRequests.get(request);
+ private NetworkRequestInfo getNriForAppRequest(
+ NetworkRequest request, int callingUid, String requestedOperation) {
+ final NetworkRequestInfo nri = mNetworkRequests.get(request);
+
if (nri != null) {
if (Process.SYSTEM_UID != callingUid && nri.mUid != callingUid) {
- if (DBG) log("Attempt to release unowned NetworkRequest " + request);
- return;
+ log(String.format("UID %d attempted to %s for unowned request %s",
+ callingUid, requestedOperation, nri));
+ return null;
}
- if (VDBG || (DBG && nri.isRequest())) log("releasing NetworkRequest " + request);
- nri.unlinkDeathRecipient();
- mNetworkRequests.remove(request);
- synchronized (mUidToNetworkRequestCount) {
- int requests = mUidToNetworkRequestCount.get(nri.mUid, 0);
- if (requests < 1) {
- Slog.wtf(TAG, "BUG: too small request count " + requests + " for UID " +
- nri.mUid);
- } else if (requests == 1) {
- mUidToNetworkRequestCount.removeAt(
- mUidToNetworkRequestCount.indexOfKey(nri.mUid));
- } else {
- mUidToNetworkRequestCount.put(nri.mUid, requests - 1);
- }
- }
- mNetworkRequestInfoLogs.log("RELEASE " + nri);
- if (nri.isRequest()) {
- // Find all networks that are satisfying this request and remove the request
- // from their request lists.
- // TODO - it's my understanding that for a request there is only a single
- // network satisfying it, so this loop is wasteful
- boolean wasKept = false;
- for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
- if (nai.networkRequests.get(nri.request.requestId) != null) {
- nai.networkRequests.remove(nri.request.requestId);
- if (VDBG) {
- log(" Removing from current network " + nai.name() +
- ", leaving " + nai.networkRequests.size() +
- " requests.");
- }
- if (unneeded(nai)) {
- if (DBG) log("no live requests for " + nai.name() + "; disconnecting");
- teardownUnneededNetwork(nai);
- } else {
- // suspect there should only be one pass through here
- // but if any were kept do the check below
- wasKept |= true;
- }
- }
- }
-
- NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
- if (nai != null) {
- mNetworkForRequestId.remove(nri.request.requestId);
- }
- // Maintain the illusion. When this request arrived, we might have pretended
- // that a network connected to serve it, even though the network was already
- // connected. Now that this request has gone away, we might have to pretend
- // that the network disconnected. LegacyTypeTracker will generate that
- // phantom disconnect for this type.
- if (nri.request.legacyType != TYPE_NONE && nai != null) {
- boolean doRemove = true;
- if (wasKept) {
- // check if any of the remaining requests for this network are for the
- // same legacy type - if so, don't remove the nai
- for (int i = 0; i < nai.networkRequests.size(); i++) {
- NetworkRequest otherRequest = nai.networkRequests.valueAt(i);
- if (otherRequest.legacyType == nri.request.legacyType &&
- isRequest(otherRequest)) {
- if (DBG) log(" still have other legacy request - leaving");
- doRemove = false;
- }
- }
- }
-
- if (doRemove) {
- mLegacyTypeTracker.remove(nri.request.legacyType, nai, false);
- }
- }
-
- for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
- nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST,
- nri.request);
- }
- } else {
- // listens don't have a singular affectedNetwork. Check all networks to see
- // if this listen request applies and remove it.
- for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
- nai.networkRequests.remove(nri.request.requestId);
- if (nri.request.networkCapabilities.hasSignalStrength() &&
- nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
- updateSignalStrengthThresholds(nai, "RELEASE", nri.request);
- }
- }
- }
- callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_RELEASED);
}
+
+ return nri;
}
+ private void handleRequestCallbackUpdate(NetworkRequest request, int callingUid,
+ String description, int callbackType) {
+ final NetworkRequestInfo nri = getNriForAppRequest(request, callingUid, description);
+ if (nri == null) return;
+
+ final NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
+ // The network that is satisfying this request may have changed since
+ // the application requested the update.
+ //
+ // - If the request is no longer satisfied, don't send any updates.
+ // - If the request is satisfied by a different network, it is the
+ // caller's responsibility to check that the Network object in the
+ // callback matches the network that was returned in the last
+ // onAvailable() callback for this request.
+ if (nai == null) return;
+ callCallbackForRequest(nri, nai, callbackType, 0);
+ }
+
+ private void handleRequestLinkProperties(NetworkRequest request, int callingUid) {
+ handleRequestCallbackUpdate(request, callingUid,
+ "request LinkProperties", ConnectivityManager.CALLBACK_IP_CHANGED);
+ }
+
+ private void handleRequestNetworkCapabilities(NetworkRequest request, int callingUid) {
+ handleRequestCallbackUpdate(request, callingUid,
+ "request NetworkCapabilities", ConnectivityManager.CALLBACK_CAP_CHANGED);
+ }
+
+ private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid) {
+ final NetworkRequestInfo nri = getNriForAppRequest(
+ request, callingUid, "release NetworkRequest");
+ if (nri == null) return;
+
+ if (VDBG || (DBG && nri.request.isRequest())) log("releasing " + request);
+ nri.unlinkDeathRecipient();
+ mNetworkRequests.remove(request);
+ synchronized (mUidToNetworkRequestCount) {
+ int requests = mUidToNetworkRequestCount.get(nri.mUid, 0);
+ if (requests < 1) {
+ Slog.wtf(TAG, "BUG: too small request count " + requests + " for UID " +
+ nri.mUid);
+ } else if (requests == 1) {
+ mUidToNetworkRequestCount.removeAt(
+ mUidToNetworkRequestCount.indexOfKey(nri.mUid));
+ } else {
+ mUidToNetworkRequestCount.put(nri.mUid, requests - 1);
+ }
+ }
+ mNetworkRequestInfoLogs.log("RELEASE " + nri);
+ if (nri.request.isRequest()) {
+ boolean wasKept = false;
+ NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
+ if (nai != null) {
+ nai.removeRequest(nri.request.requestId);
+ if (VDBG) {
+ log(" Removing from current network " + nai.name() +
+ ", leaving " + nai.numNetworkRequests() + " requests.");
+ }
+ // If there are still lingered requests on this network, don't tear it down,
+ // but resume lingering instead.
+ updateLingerState(nai, SystemClock.elapsedRealtime());
+ if (unneeded(nai)) {
+ if (DBG) log("no live requests for " + nai.name() + "; disconnecting");
+ teardownUnneededNetwork(nai);
+ } else {
+ wasKept = true;
+ }
+ mNetworkForRequestId.remove(nri.request.requestId);
+ }
+
+ // TODO: remove this code once we know that the Slog.wtf is never hit.
+ //
+ // Find all networks that are satisfying this request and remove the request
+ // from their request lists.
+ // TODO - it's my understanding that for a request there is only a single
+ // network satisfying it, so this loop is wasteful
+ for (NetworkAgentInfo otherNai : mNetworkAgentInfos.values()) {
+ if (otherNai.isSatisfyingRequest(nri.request.requestId) && otherNai != nai) {
+ Slog.wtf(TAG, "Request " + nri.request + " satisfied by " +
+ otherNai.name() + ", but mNetworkAgentInfos says " +
+ (nai != null ? nai.name() : "null"));
+ }
+ }
+
+ // Maintain the illusion. When this request arrived, we might have pretended
+ // that a network connected to serve it, even though the network was already
+ // connected. Now that this request has gone away, we might have to pretend
+ // that the network disconnected. LegacyTypeTracker will generate that
+ // phantom disconnect for this type.
+ if (nri.request.legacyType != TYPE_NONE && nai != null) {
+ boolean doRemove = true;
+ if (wasKept) {
+ // check if any of the remaining requests for this network are for the
+ // same legacy type - if so, don't remove the nai
+ for (int i = 0; i < nai.numNetworkRequests(); i++) {
+ NetworkRequest otherRequest = nai.requestAt(i);
+ if (otherRequest.legacyType == nri.request.legacyType &&
+ otherRequest.isRequest()) {
+ if (DBG) log(" still have other legacy request - leaving");
+ doRemove = false;
+ }
+ }
+ }
+
+ if (doRemove) {
+ mLegacyTypeTracker.remove(nri.request.legacyType, nai, false);
+ }
+ }
+
+ for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
+ nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST,
+ nri.request);
+ }
+ } else {
+ // listens don't have a singular affectedNetwork. Check all networks to see
+ // if this listen request applies and remove it.
+ for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+ nai.removeRequest(nri.request.requestId);
+ if (nri.request.networkCapabilities.hasSignalStrength() &&
+ nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
+ updateSignalStrengthThresholds(nai, "RELEASE", nri.request);
+ }
+ }
+ }
+ callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_RELEASED, 0);
+ }
+
+ @Override
public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
enforceConnectivityInternalPermission();
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_ACCEPT_UNVALIDATED,
@@ -2562,8 +2716,9 @@
PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
- setProvNotificationVisibleIntent(true, nai.network.netId, NotificationType.NO_INTERNET,
- nai.networkInfo.getType(), nai.networkInfo.getExtraInfo(), pendingIntent, true);
+
+ mNotifier.showNotification(nai.network.netId, NotificationType.NO_INTERNET, nai, null,
+ pendingIntent, true);
}
private class InternalHandler extends Handler {
@@ -2647,6 +2802,12 @@
handleMobileDataAlwaysOn();
break;
}
+ case EVENT_REQUEST_LINKPROPERTIES:
+ handleRequestLinkProperties((NetworkRequest) msg.obj, msg.arg1);
+ break;
+ case EVENT_REQUEST_NETCAPABILITIES:
+ handleRequestNetworkCapabilities((NetworkRequest) msg.obj, msg.arg1);
+ break;
// Sent by KeepaliveTracker to process an app request on the state machine thread.
case NetworkAgent.CMD_START_PACKET_KEEPALIVE: {
mKeepaliveTracker.handleStartKeepalive(msg);
@@ -2671,6 +2832,7 @@
}
// javadoc from interface
+ @Override
public int tether(String iface) {
ConnectivityManager.enforceTetherChangePermission(mContext);
if (isTetheringSupported()) {
@@ -2688,6 +2850,7 @@
}
// javadoc from interface
+ @Override
public int untether(String iface) {
ConnectivityManager.enforceTetherChangePermission(mContext);
@@ -2706,6 +2869,7 @@
}
// javadoc from interface
+ @Override
public int getLastTetherError(String iface) {
enforceTetherAccessPermission();
@@ -2717,6 +2881,7 @@
}
// TODO - proper iface API for selection by property, inspection, etc
+ @Override
public String[] getTetherableUsbRegexs() {
enforceTetherAccessPermission();
if (isTetheringSupported()) {
@@ -2726,6 +2891,7 @@
}
}
+ @Override
public String[] getTetherableWifiRegexs() {
enforceTetherAccessPermission();
if (isTetheringSupported()) {
@@ -2735,6 +2901,7 @@
}
}
+ @Override
public String[] getTetherableBluetoothRegexs() {
enforceTetherAccessPermission();
if (isTetheringSupported()) {
@@ -2744,6 +2911,7 @@
}
}
+ @Override
public int setUsbTethering(boolean enable) {
ConnectivityManager.enforceTetherChangePermission(mContext);
if (isTetheringSupported()) {
@@ -2755,21 +2923,25 @@
// TODO - move iface listing, queries, etc to new module
// javadoc from interface
+ @Override
public String[] getTetherableIfaces() {
enforceTetherAccessPermission();
return mTethering.getTetherableIfaces();
}
+ @Override
public String[] getTetheredIfaces() {
enforceTetherAccessPermission();
return mTethering.getTetheredIfaces();
}
+ @Override
public String[] getTetheringErroredIfaces() {
enforceTetherAccessPermission();
return mTethering.getErroredIfaces();
}
+ @Override
public String[] getTetheredDhcpRanges() {
enforceConnectivityInternalPermission();
return mTethering.getTetheredDhcpRanges();
@@ -2828,12 +3000,14 @@
}
// 100 percent is full good, 0 is full bad.
+ @Override
public void reportInetCondition(int networkType, int percentage) {
NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
if (nai == null) return;
reportNetworkConnectivity(nai.network, percentage > 50);
}
+ @Override
public void reportNetworkConnectivity(Network network, boolean hasConnectivity) {
enforceAccessPermission();
enforceInternetPermission();
@@ -2878,6 +3052,7 @@
}
}
+ @Override
public ProxyInfo getProxyForNetwork(Network network) {
if (network == null) return getDefaultProxy();
final ProxyInfo globalProxy = getGlobalProxy();
@@ -3449,118 +3624,6 @@
return -1;
}
- private static final String NOTIFICATION_ID = "CaptivePortal.Notification";
- private static enum NotificationType { SIGN_IN, NO_INTERNET; };
-
- private void setProvNotificationVisible(boolean visible, int networkType, String action) {
- Intent intent = new Intent(action);
- PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
- // Concatenate the range of types onto the range of NetIDs.
- int id = MAX_NET_ID + 1 + (networkType - ConnectivityManager.TYPE_NONE);
- setProvNotificationVisibleIntent(visible, id, NotificationType.SIGN_IN,
- networkType, null, pendingIntent, false);
- }
-
- /**
- * Show or hide network provisioning notifications.
- *
- * We use notifications for two purposes: to notify that a network requires sign in
- * (NotificationType.SIGN_IN), or to notify that a network does not have Internet access
- * (NotificationType.NO_INTERNET). We display at most one notification per ID, so on a
- * particular network we can display the notification type that was most recently requested.
- * So for example if a captive portal fails to reply within a few seconds of connecting, we
- * might first display NO_INTERNET, and then when the captive portal check completes, display
- * SIGN_IN.
- *
- * @param id an identifier that uniquely identifies this notification. This must match
- * between show and hide calls. We use the NetID value but for legacy callers
- * we concatenate the range of types with the range of NetIDs.
- */
- private void setProvNotificationVisibleIntent(boolean visible, int id,
- NotificationType notifyType, int networkType, String extraInfo, PendingIntent intent,
- boolean highPriority) {
- if (VDBG || (DBG && visible)) {
- log("setProvNotificationVisibleIntent " + notifyType + " visible=" + visible
- + " networkType=" + getNetworkTypeName(networkType)
- + " extraInfo=" + extraInfo + " highPriority=" + highPriority);
- }
-
- Resources r = Resources.getSystem();
- NotificationManager notificationManager = (NotificationManager) mContext
- .getSystemService(Context.NOTIFICATION_SERVICE);
-
- if (visible) {
- CharSequence title;
- CharSequence details;
- int icon;
- if (notifyType == NotificationType.NO_INTERNET &&
- networkType == ConnectivityManager.TYPE_WIFI) {
- title = r.getString(R.string.wifi_no_internet, 0);
- details = r.getString(R.string.wifi_no_internet_detailed);
- icon = R.drawable.stat_notify_wifi_in_range; // TODO: Need new icon.
- } else if (notifyType == NotificationType.SIGN_IN) {
- switch (networkType) {
- case ConnectivityManager.TYPE_WIFI:
- title = r.getString(R.string.wifi_available_sign_in, 0);
- details = r.getString(R.string.network_available_sign_in_detailed,
- extraInfo);
- icon = R.drawable.stat_notify_wifi_in_range;
- break;
- case ConnectivityManager.TYPE_MOBILE:
- case ConnectivityManager.TYPE_MOBILE_HIPRI:
- title = r.getString(R.string.network_available_sign_in, 0);
- // TODO: Change this to pull from NetworkInfo once a printable
- // name has been added to it
- details = mTelephonyManager.getNetworkOperatorName();
- icon = R.drawable.stat_notify_rssi_in_range;
- break;
- default:
- title = r.getString(R.string.network_available_sign_in, 0);
- details = r.getString(R.string.network_available_sign_in_detailed,
- extraInfo);
- icon = R.drawable.stat_notify_rssi_in_range;
- break;
- }
- } else {
- Slog.wtf(TAG, "Unknown notification type " + notifyType + "on network type "
- + getNetworkTypeName(networkType));
- return;
- }
-
- Notification notification = new Notification.Builder(mContext)
- .setWhen(0)
- .setSmallIcon(icon)
- .setAutoCancel(true)
- .setTicker(title)
- .setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setContentTitle(title)
- .setContentText(details)
- .setContentIntent(intent)
- .setLocalOnly(true)
- .setPriority(highPriority ?
- Notification.PRIORITY_HIGH :
- Notification.PRIORITY_DEFAULT)
- .setDefaults(highPriority ? Notification.DEFAULT_ALL : 0)
- .setOnlyAlertOnce(true)
- .build();
-
- try {
- notificationManager.notifyAsUser(NOTIFICATION_ID, id, notification, UserHandle.ALL);
- } catch (NullPointerException npe) {
- loge("setNotificationVisible: visible notificationManager npe=" + npe);
- npe.printStackTrace();
- }
- } else {
- try {
- notificationManager.cancelAsUser(NOTIFICATION_ID, id, UserHandle.ALL);
- } catch (NullPointerException npe) {
- loge("setNotificationVisible: cancel notificationManager npe=" + npe);
- npe.printStackTrace();
- }
- }
- }
-
/** Location to an updatable file listing carrier provisioning urls.
* An example:
*
@@ -3664,7 +3727,9 @@
enforceConnectivityInternalPermission();
final long ident = Binder.clearCallingIdentity();
try {
- setProvNotificationVisible(visible, networkType, action);
+ // Concatenate the range of types onto the range of NetIDs.
+ int id = MAX_NET_ID + 1 + (networkType - ConnectivityManager.TYPE_NONE);
+ mNotifier.setProvNotificationVisible(visible, id, action);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -3793,31 +3858,12 @@
}
}
- /**
- * A NetworkRequest as registered by an application can be one of three
- * types:
- *
- * - "listen", for which the framework will issue callbacks about any
- * and all networks that match the specified NetworkCapabilities,
- *
- * - "request", capable of causing a specific network to be created
- * first (e.g. a telephony DUN request), the framework will issue
- * callbacks about the single, highest scoring current network
- * (if any) that matches the specified NetworkCapabilities, or
- *
- * - "track the default network", a hybrid of the two designed such
- * that the framework will issue callbacks for the single, highest
- * scoring current network (if any) that matches the capabilities of
- * the default Internet request (mDefaultRequest), but which cannot
- * cause the framework to either create or retain the existence of
- * any specific network.
- *
- */
- private static enum NetworkRequestType {
- LISTEN,
- TRACK_DEFAULT,
- REQUEST
- };
+ private void ensureNetworkRequestHasType(NetworkRequest request) {
+ if (request.type == NetworkRequest.Type.NONE) {
+ throw new IllegalArgumentException(
+ "All NetworkRequests in ConnectivityService must have a type");
+ }
+ }
/**
* Tracks info about the requester.
@@ -3831,27 +3877,26 @@
final int mPid;
final int mUid;
final Messenger messenger;
- private final NetworkRequestType mType;
- NetworkRequestInfo(NetworkRequest r, PendingIntent pi, NetworkRequestType type) {
+ NetworkRequestInfo(NetworkRequest r, PendingIntent pi) {
request = r;
+ ensureNetworkRequestHasType(request);
mPendingIntent = pi;
messenger = null;
mBinder = null;
mPid = getCallingPid();
mUid = getCallingUid();
- mType = type;
enforceRequestCountLimit();
}
- NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder, NetworkRequestType type) {
+ NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder) {
super();
messenger = m;
request = r;
+ ensureNetworkRequestHasType(request);
mBinder = binder;
mPid = getCallingPid();
mUid = getCallingUid();
- mType = type;
mPendingIntent = null;
enforceRequestCountLimit();
@@ -3872,16 +3917,6 @@
}
}
- private String typeString() {
- switch (mType) {
- case LISTEN: return "Listen";
- case REQUEST: return "Request";
- case TRACK_DEFAULT: return "Track default";
- default:
- return "unknown type";
- }
- }
-
void unlinkDeathRecipient() {
if (mBinder != null) {
mBinder.unlinkToDeath(this, 0);
@@ -3894,29 +3929,8 @@
releaseNetworkRequest(request);
}
- /**
- * Returns true iff. the contained NetworkRequest is one that:
- *
- * - should be associated with at most one satisfying network
- * at a time;
- *
- * - should cause a network to be kept up if it is the only network
- * which can satisfy the NetworkReqeust.
- *
- * For full detail of how isRequest() is used for pairing Networks with
- * NetworkRequests read rematchNetworkAndRequests().
- *
- * TODO: Rename to something more properly descriptive.
- */
- public boolean isRequest() {
- return (mType == NetworkRequestType.TRACK_DEFAULT) ||
- (mType == NetworkRequestType.REQUEST);
- }
-
public String toString() {
- return typeString() +
- " from uid/pid:" + mUid + "/" + mPid +
- " for " + request +
+ return "uid/pid:" + mUid + "/" + mPid + " " + request +
(mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
}
}
@@ -3966,18 +3980,21 @@
@Override
public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
Messenger messenger, int timeoutMs, IBinder binder, int legacyType) {
- final NetworkRequestType type = (networkCapabilities == null)
- ? NetworkRequestType.TRACK_DEFAULT
- : NetworkRequestType.REQUEST;
+ final NetworkRequest.Type type = (networkCapabilities == null)
+ ? NetworkRequest.Type.TRACK_DEFAULT
+ : NetworkRequest.Type.REQUEST;
// If the requested networkCapabilities is null, take them instead from
// the default network request. This allows callers to keep track of
// the system default network.
- if (type == NetworkRequestType.TRACK_DEFAULT) {
+ if (type == NetworkRequest.Type.TRACK_DEFAULT) {
networkCapabilities = new NetworkCapabilities(mDefaultRequest.networkCapabilities);
enforceAccessPermission();
} else {
networkCapabilities = new NetworkCapabilities(networkCapabilities);
enforceNetworkRequestPermissions(networkCapabilities);
+ // TODO: this is incorrect. We mark the request as metered or not depending on the state
+ // of the app when the request is filed, but we never change the request if the app
+ // changes network state. http://b/29964605
enforceMeteredApnPolicy(networkCapabilities);
}
ensureRequestableCapabilities(networkCapabilities);
@@ -3993,8 +4010,8 @@
}
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
- nextNetworkRequestId());
- NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder, type);
+ nextNetworkRequestId(), type);
+ NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder);
if (DBG) log("requestNetwork for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri));
@@ -4007,7 +4024,7 @@
private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities) {
if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) == false) {
- enforceConnectivityInternalPermission();
+ enforceConnectivityRestrictedNetworksPermission();
} else {
enforceChangePermission();
}
@@ -4064,9 +4081,8 @@
ensureRequestableCapabilities(networkCapabilities);
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
- nextNetworkRequestId());
- NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation,
- NetworkRequestType.REQUEST);
+ nextNetworkRequestId(), NetworkRequest.Type.REQUEST);
+ NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation);
if (DBG) log("pendingRequest for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT,
nri));
@@ -4116,9 +4132,9 @@
}
NetworkRequest networkRequest = new NetworkRequest(
- new NetworkCapabilities(networkCapabilities), TYPE_NONE, nextNetworkRequestId());
- NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
- NetworkRequestType.LISTEN);
+ new NetworkCapabilities(networkCapabilities), TYPE_NONE, nextNetworkRequestId(),
+ NetworkRequest.Type.LISTEN);
+ NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder);
if (VDBG) log("listenForNetwork for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
@@ -4134,18 +4150,35 @@
}
NetworkRequest networkRequest = new NetworkRequest(
- new NetworkCapabilities(networkCapabilities), TYPE_NONE, nextNetworkRequestId());
- NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation,
- NetworkRequestType.LISTEN);
+ new NetworkCapabilities(networkCapabilities), TYPE_NONE, nextNetworkRequestId(),
+ NetworkRequest.Type.LISTEN);
+ NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation);
if (VDBG) log("pendingListenForNetwork for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
}
@Override
+ public void requestLinkProperties(NetworkRequest networkRequest) {
+ ensureNetworkRequestHasType(networkRequest);
+ if (networkRequest.type == NetworkRequest.Type.LISTEN) return;
+ mHandler.sendMessage(mHandler.obtainMessage(
+ EVENT_REQUEST_LINKPROPERTIES, getCallingUid(), 0, networkRequest));
+ }
+
+ @Override
+ public void requestNetworkCapabilities(NetworkRequest networkRequest) {
+ ensureNetworkRequestHasType(networkRequest);
+ if (networkRequest.type == NetworkRequest.Type.LISTEN) return;
+ mHandler.sendMessage(mHandler.obtainMessage(
+ EVENT_REQUEST_NETCAPABILITIES, getCallingUid(), 0, networkRequest));
+ }
+
+ @Override
public void releaseNetworkRequest(NetworkRequest networkRequest) {
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST, getCallingUid(),
- 0, networkRequest));
+ ensureNetworkRequestHasType(networkRequest);
+ mHandler.sendMessage(mHandler.obtainMessage(
+ EVENT_RELEASE_NETWORK_REQUEST, getCallingUid(), 0, networkRequest));
}
@Override
@@ -4220,6 +4253,10 @@
return nai == getDefaultNetwork();
}
+ private boolean isDefaultRequest(NetworkRequestInfo nri) {
+ return nri.request.requestId == mDefaultRequest.requestId;
+ }
+
public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
int currentScore, NetworkMisc networkMisc) {
@@ -4452,10 +4489,10 @@
}
private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) {
- for (int i = 0; i < nai.networkRequests.size(); i++) {
- NetworkRequest nr = nai.networkRequests.valueAt(i);
+ for (int i = 0; i < nai.numNetworkRequests(); i++) {
+ NetworkRequest nr = nai.requestAt(i);
// Don't send listening requests to factories. b/17393458
- if (!isRequest(nr)) continue;
+ if (nr.isListen()) continue;
sendUpdatedScoreToFactories(nr, nai.getCurrentScore());
}
}
@@ -4504,7 +4541,7 @@
}
private void callCallbackForRequest(NetworkRequestInfo nri,
- NetworkAgentInfo networkAgent, int notificationType) {
+ NetworkAgentInfo networkAgent, int notificationType, int arg1) {
if (nri.messenger == null) return; // Default request has no msgr
Bundle bundle = new Bundle();
bundle.putParcelable(NetworkRequest.class.getSimpleName(),
@@ -4516,7 +4553,7 @@
}
switch (notificationType) {
case ConnectivityManager.CALLBACK_LOSING: {
- msg.arg1 = 30 * 1000; // TODO - read this from NetworkMonitor
+ msg.arg1 = arg1;
break;
}
case ConnectivityManager.CALLBACK_CAP_CHANGED: {
@@ -4545,12 +4582,14 @@
}
private void teardownUnneededNetwork(NetworkAgentInfo nai) {
- for (int i = 0; i < nai.networkRequests.size(); i++) {
- NetworkRequest nr = nai.networkRequests.valueAt(i);
- // Ignore listening requests.
- if (!isRequest(nr)) continue;
- loge("Dead network still had at least " + nr);
- break;
+ if (nai.numRequestNetworkRequests() != 0) {
+ for (int i = 0; i < nai.numNetworkRequests(); i++) {
+ NetworkRequest nr = nai.requestAt(i);
+ // Ignore listening requests.
+ if (nr.isListen()) continue;
+ loge("Dead network still had at least " + nr);
+ break;
+ }
}
nai.asyncChannel.disconnect();
}
@@ -4561,7 +4600,14 @@
return;
}
if (DBG) log("handleLingerComplete for " + oldNetwork.name());
- teardownUnneededNetwork(oldNetwork);
+
+ // If we get here it means that the last linger timeout for this network expired. So there
+ // must be no other active linger timers, and we must stop lingering.
+ oldNetwork.clearLingerState();
+
+ if (unneeded(oldNetwork)) {
+ teardownUnneededNetwork(oldNetwork);
+ }
}
private void makeDefault(NetworkAgentInfo newNetwork) {
@@ -4606,7 +4652,7 @@
// performed to tear down unvalidated networks that have no chance (i.e. even if
// validated) of becoming the highest scoring network.
private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork,
- ReapUnvalidatedNetworks reapUnvalidatedNetworks) {
+ ReapUnvalidatedNetworks reapUnvalidatedNetworks, long now) {
if (!newNetwork.everConnected) return;
boolean keep = newNetwork.isVPN();
boolean isNewDefault = false;
@@ -4632,7 +4678,7 @@
// check if it satisfies the NetworkCapabilities
if (VDBG) log(" checking if request is satisfied: " + nri.request);
if (satisfies) {
- if (!nri.isRequest()) {
+ if (nri.request.isListen()) {
// This is not a request, it's a callback listener.
// Add it to newNetwork regardless of score.
if (newNetwork.addRequest(nri.request)) addedRequests.add(nri);
@@ -4651,13 +4697,16 @@
if (VDBG) log("rematch for " + newNetwork.name());
if (currentNetwork != null) {
if (VDBG) log(" accepting network in place of " + currentNetwork.name());
- currentNetwork.networkRequests.remove(nri.request.requestId);
- currentNetwork.networkLingered.add(nri.request);
+ currentNetwork.removeRequest(nri.request.requestId);
+ currentNetwork.lingerRequest(nri.request, now, mLingerDelayMs);
+ if (isDefaultRequest(nri)) {
+ mLingerMonitor.noteLingerDefaultNetwork(currentNetwork, newNetwork);
+ }
affectedNetworks.add(currentNetwork);
} else {
if (VDBG) log(" accepting network in place of null");
}
- unlinger(newNetwork);
+ newNetwork.unlingerRequest(nri.request);
mNetworkForRequestId.put(nri.request.requestId, newNetwork);
if (!newNetwork.addRequest(nri.request)) {
Slog.wtf(TAG, "BUG: " + newNetwork.name() + " already has " + nri.request);
@@ -4670,12 +4719,12 @@
// network. Think about if there is a way to reduce this. Push
// netid->request mapping to each factory?
sendUpdatedScoreToFactories(nri.request, newNetwork.getCurrentScore());
- if (mDefaultRequest.requestId == nri.request.requestId) {
+ if (isDefaultRequest(nri)) {
isNewDefault = true;
oldDefaultNetwork = currentNetwork;
}
}
- } else if (newNetwork.networkRequests.get(nri.request.requestId) != null) {
+ } else if (newNetwork.isSatisfyingRequest(nri.request.requestId)) {
// If "newNetwork" is listed as satisfying "nri" but no longer satisfies "nri",
// mark it as no longer satisfying "nri". Because networks are processed by
// rematchAllNetworkAndRequests() in descending score order, "currentNetwork" will
@@ -4687,12 +4736,12 @@
log("Network " + newNetwork.name() + " stopped satisfying" +
" request " + nri.request.requestId);
}
- newNetwork.networkRequests.remove(nri.request.requestId);
+ newNetwork.removeRequest(nri.request.requestId);
if (currentNetwork == newNetwork) {
mNetworkForRequestId.remove(nri.request.requestId);
sendUpdatedScoreToFactories(nri.request, 0);
} else {
- if (nri.isRequest()) {
+ if (nri.request.isRequest()) {
Slog.wtf(TAG, "BUG: Removing request " + nri.request.requestId + " from " +
newNetwork.name() +
" without updating mNetworkForRequestId or factories!");
@@ -4705,23 +4754,7 @@
// a) be requested and b) change is NET_CAPABILITY_TRUSTED,
// so this code is only incorrect for a network that loses
// the TRUSTED capability, which is a rare case.
- callCallbackForRequest(nri, newNetwork, ConnectivityManager.CALLBACK_LOST);
- }
- }
- // Linger any networks that are no longer needed.
- for (NetworkAgentInfo nai : affectedNetworks) {
- if (nai.lingering) {
- // Already lingered. Nothing to do. This can only happen if "nai" is in
- // "affectedNetworks" twice. The reasoning being that to get added to
- // "affectedNetworks", "nai" must have been satisfying a NetworkRequest
- // (i.e. not lingered) so it could have only been lingered by this loop.
- // unneeded(nai) will be false and we'll call unlinger() below which would
- // be bad, so handle it here.
- } else if (unneeded(nai)) {
- linger(nai);
- } else {
- // Clear nai.networkLingered we might have added above.
- unlinger(nai);
+ callCallbackForRequest(nri, newNetwork, ConnectivityManager.CALLBACK_LOST, 0);
}
}
if (isNewDefault) {
@@ -4746,6 +4779,15 @@
// before LegacyTypeTracker sends legacy broadcasts
for (NetworkRequestInfo nri : addedRequests) notifyNetworkCallback(newNetwork, nri);
+ // Linger any networks that are no longer needed. This should be done after sending the
+ // available callback for newNetwork.
+ for (NetworkAgentInfo nai : affectedNetworks) {
+ updateLingerState(nai, now);
+ }
+ // Possibly unlinger newNetwork. Unlingering a network does not send any callbacks so it
+ // does not need to be done in any particular order.
+ updateLingerState(newNetwork, now);
+
if (isNewDefault) {
// Maintain the illusion: since the legacy API only
// understands one network at a time, we must pretend
@@ -4792,9 +4834,9 @@
// (notification callbacks) and then uses the old api (getNetworkInfo(type))
// they may get old info. Reverse this after the old startUsing api is removed.
// This is on top of the multiple intent sequencing referenced in the todo above.
- for (int i = 0; i < newNetwork.networkRequests.size(); i++) {
- NetworkRequest nr = newNetwork.networkRequests.valueAt(i);
- if (nr.legacyType != TYPE_NONE && isRequest(nr)) {
+ for (int i = 0; i < newNetwork.numNetworkRequests(); i++) {
+ NetworkRequest nr = newNetwork.requestAt(i);
+ if (nr.legacyType != TYPE_NONE && nr.isRequest()) {
// legacy type tracker filters out repeat adds
mLegacyTypeTracker.add(nr.legacyType, newNetwork);
}
@@ -4811,8 +4853,19 @@
if (reapUnvalidatedNetworks == ReapUnvalidatedNetworks.REAP) {
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
if (unneeded(nai)) {
- if (DBG) log("Reaping " + nai.name());
- teardownUnneededNetwork(nai);
+ if (nai.getLingerExpiry() > 0) {
+ // This network has active linger timers and no requests, but is not
+ // lingering. Linger it.
+ //
+ // One way (the only way?) this can happen if this network is unvalidated
+ // and became unneeded due to another network improving its score to the
+ // point where this network will no longer be able to satisfy any requests
+ // even if it validates.
+ updateLingerState(nai, now);
+ } else {
+ if (DBG) log("Reaping " + nai.name());
+ teardownUnneededNetwork(nai);
+ }
}
}
}
@@ -4839,8 +4892,9 @@
// Optimization: Only reprocess "changed" if its score improved. This is safe because it
// can only add more NetworkRequests satisfied by "changed", and this is exactly what
// rematchNetworkAndRequests() handles.
+ final long now = SystemClock.elapsedRealtime();
if (changed != null && oldScore < changed.getCurrentScore()) {
- rematchNetworkAndRequests(changed, ReapUnvalidatedNetworks.REAP);
+ rematchNetworkAndRequests(changed, ReapUnvalidatedNetworks.REAP, now);
} else {
final NetworkAgentInfo[] nais = mNetworkAgentInfos.values().toArray(
new NetworkAgentInfo[mNetworkAgentInfos.size()]);
@@ -4854,7 +4908,8 @@
// is complete could incorrectly teardown a network that hasn't yet been
// rematched.
(nai != nais[nais.length-1]) ? ReapUnvalidatedNetworks.DONT_REAP
- : ReapUnvalidatedNetworks.REAP);
+ : ReapUnvalidatedNetworks.REAP,
+ now);
}
}
}
@@ -4964,7 +5019,8 @@
updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
// Consider network even though it is not yet validated.
- rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP);
+ final long now = SystemClock.elapsedRealtime();
+ rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP, now);
// This has to happen after matching the requests, because callbacks are just requests.
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
@@ -5012,14 +5068,8 @@
// notify only this one new request of the current state
protected void notifyNetworkCallback(NetworkAgentInfo nai, NetworkRequestInfo nri) {
int notifyType = ConnectivityManager.CALLBACK_AVAILABLE;
- // TODO - read state from monitor to decide what to send.
-// if (nai.networkMonitor.isLingering()) {
-// notifyType = NetworkCallbacks.LOSING;
-// } else if (nai.networkMonitor.isEvaluating()) {
-// notifyType = NetworkCallbacks.callCallbackForRequest(request, nai, notifyType);
-// }
if (nri.mPendingIntent == null) {
- callCallbackForRequest(nri, nai, notifyType);
+ callCallbackForRequest(nri, nai, notifyType, 0);
} else {
sendPendingIntentForRequest(nri, nai, notifyType);
}
@@ -5053,7 +5103,7 @@
intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
}
NetworkAgentInfo newDefaultAgent = null;
- if (nai.networkRequests.get(mDefaultRequest.requestId) != null) {
+ if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
newDefaultAgent = getDefaultNetwork();
if (newDefaultAgent != null) {
intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO,
@@ -5071,20 +5121,24 @@
}
}
- protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType) {
+ protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType, int arg1) {
if (VDBG) log("notifyType " + notifyTypeToName(notifyType) + " for " + networkAgent.name());
- for (int i = 0; i < networkAgent.networkRequests.size(); i++) {
- NetworkRequest nr = networkAgent.networkRequests.valueAt(i);
+ for (int i = 0; i < networkAgent.numNetworkRequests(); i++) {
+ NetworkRequest nr = networkAgent.requestAt(i);
NetworkRequestInfo nri = mNetworkRequests.get(nr);
if (VDBG) log(" sending notification for " + nr);
if (nri.mPendingIntent == null) {
- callCallbackForRequest(nri, networkAgent, notifyType);
+ callCallbackForRequest(nri, networkAgent, notifyType, arg1);
} else {
sendPendingIntentForRequest(nri, networkAgent, notifyType);
}
}
}
+ protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType) {
+ notifyNetworkCallbacks(networkAgent, notifyType, 0);
+ }
+
private String notifyTypeToName(int notifyType) {
switch (notifyType) {
case ConnectivityManager.CALLBACK_PRECHECK: return "PRECHECK";
@@ -5215,7 +5269,12 @@
return new NetworkMonitor(context, handler, nai, defaultRequest);
}
- private static void logDefaultNetworkEvent(NetworkAgentInfo newNai, NetworkAgentInfo prevNai) {
+ @VisibleForTesting
+ public WakeupMessage makeWakeupMessage(Context c, Handler h, String s, int cmd, Object obj) {
+ return new WakeupMessage(c, h, s, cmd, 0, 0, obj);
+ }
+
+ private void logDefaultNetworkEvent(NetworkAgentInfo newNai, NetworkAgentInfo prevNai) {
int newNetid = NETID_UNSET;
int prevNetid = NETID_UNSET;
int[] transports = new int[0];
@@ -5233,6 +5292,10 @@
hadIPv6 = lp.hasGlobalIPv6Address() && lp.hasIPv6DefaultRoute();
}
- DefaultNetworkEvent.logEvent(newNetid, transports, prevNetid, hadIPv4, hadIPv6);
+ mMetricsLog.log(new DefaultNetworkEvent(newNetid, transports, prevNetid, hadIPv4, hadIPv6));
+ }
+
+ private void logNetworkEvent(NetworkAgentInfo nai, int evtype) {
+ mMetricsLog.log(new NetworkEvent(nai.network.netId, evtype));
}
}
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index afed5ef..488f0e7 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -973,13 +973,12 @@
cancelSensingTimeoutAlarmLocked();
}
}
- if (result == AnyMotionDetector.RESULT_MOVED) {
- if (DEBUG) Slog.d(TAG, "RESULT_MOVED received.");
+ if ((result == AnyMotionDetector.RESULT_MOVED) ||
+ (result == AnyMotionDetector.RESULT_UNKNOWN)) {
synchronized (this) {
- handleMotionDetectedLocked(mConstants.INACTIVE_TIMEOUT, "sense_motion");
+ handleMotionDetectedLocked(mConstants.INACTIVE_TIMEOUT, "non_stationary");
}
} else if (result == AnyMotionDetector.RESULT_STATIONARY) {
- if (DEBUG) Slog.d(TAG, "RESULT_STATIONARY received.");
if (mState == STATE_SENSING) {
// If we are currently sensing, it is time to move to locating.
synchronized (this) {
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 5a90488..553cb07 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -33,6 +33,7 @@
import android.os.PowerManager.WakeLock;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.MutableBoolean;
import android.util.Slog;
@@ -284,8 +285,8 @@
* @return true if camera was launched, false otherwise.
*/
private boolean handleCameraLaunchGesture(boolean useWakelock, int source) {
- boolean userSetupComplete = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
+ boolean userSetupComplete = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
if (!userSetupComplete) {
if (DBG) Slog.d(TAG, String.format(
"userSetupComplete = %s, ignoring camera launch gesture.",
diff --git a/services/core/java/com/android/server/InputContentUriTokenHandler.java b/services/core/java/com/android/server/InputContentUriTokenHandler.java
new file mode 100644
index 0000000..3f4972b
--- /dev/null
+++ b/services/core/java/com/android/server/InputContentUriTokenHandler.java
@@ -0,0 +1,121 @@
+/*
+** 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 com.android.server;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.ActivityManagerNative;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.IInputContentUriToken;
+
+final class InputContentUriTokenHandler extends IInputContentUriToken.Stub {
+
+ @NonNull
+ private final Uri mUri;
+ private final int mSourceUid;
+ @NonNull
+ private final String mTargetPackage;
+ @UserIdInt
+ private final int mSourceUserId;
+ @UserIdInt
+ private final int mTargetUserId;
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private IBinder mPermissionOwnerToken = null;
+
+ InputContentUriTokenHandler(@NonNull Uri contentUri, int sourceUid,
+ @NonNull String targetPackage, @UserIdInt int sourceUserId,
+ @UserIdInt int targetUserId) {
+ mUri = contentUri;
+ mSourceUid = sourceUid;
+ mTargetPackage = targetPackage;
+ mSourceUserId = sourceUserId;
+ mTargetUserId = targetUserId;
+ }
+
+ @Override
+ public void take() {
+ synchronized (mLock) {
+ if (mPermissionOwnerToken != null) {
+ // Permission is already granted.
+ return;
+ }
+
+ try {
+ mPermissionOwnerToken = ActivityManagerNative.getDefault()
+ .newUriPermissionOwner("InputContentUriTokenHandler");
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+
+ doTakeLocked(mPermissionOwnerToken);
+ }
+ }
+
+ private void doTakeLocked(@NonNull IBinder permissionOwner) {
+ long origId = Binder.clearCallingIdentity();
+ try {
+ try {
+ ActivityManagerNative.getDefault().grantUriPermissionFromOwner(
+ permissionOwner, mSourceUid, mTargetPackage, mUri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION, mSourceUserId, mTargetUserId);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public void release() {
+ synchronized (mLock) {
+ if (mPermissionOwnerToken == null) {
+ return;
+ }
+ try {
+ ActivityManagerNative.getDefault().revokeUriPermissionFromOwner(
+ mPermissionOwnerToken, mUri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION, mSourceUserId);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ } finally {
+ mPermissionOwnerToken = null;
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ release();
+ } finally {
+ super.finalize();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 5d8fe7c..71ac544 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -18,6 +18,7 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController;
import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
import com.android.internal.inputmethod.InputMethodUtils;
@@ -137,6 +138,7 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.nio.charset.StandardCharsets;
+import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -175,6 +177,8 @@
static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
+ static final int MSG_SYSTEM_UNLOCK_USER = 5000;
+
static final long TIME_TO_RECONNECT = 3 * 1000;
static final int SECURE_SUGGESTION_SPANS_MAX_SIZE = 20;
@@ -798,14 +802,14 @@
@Override
public void onSwitchUser(@UserIdInt int userHandle) {
- // Called on the system server's main looper thread.
+ // Called on ActivityManager thread.
// TODO: Dispatch this to a worker thread as needed.
mService.onSwitchUser(userHandle);
}
@Override
public void onBootPhase(int phase) {
- // Called on the system server's main looper thread.
+ // Called on ActivityManager thread.
// TODO: Dispatch this to a worker thread as needed.
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
StatusBarManagerService statusBarService = (StatusBarManagerService) ServiceManager
@@ -815,10 +819,10 @@
}
@Override
- public void onUnlockUser(@UserIdInt int userHandle) {
- // Called on the system server's main looper thread.
- // TODO: Dispatch this to a worker thread as needed.
- mService.onUnlockUser(userHandle);
+ public void onUnlockUser(final @UserIdInt int userHandle) {
+ // Called on ActivityManager thread.
+ mService.mHandler.sendMessage(mService.mHandler.obtainMessage(MSG_SYSTEM_UNLOCK_USER,
+ userHandle));
}
}
@@ -2968,6 +2972,10 @@
case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
mHardKeyboardListener.handleHardKeyboardStatusChange(msg.arg1 == 1);
return true;
+ case MSG_SYSTEM_UNLOCK_USER:
+ final int userId = msg.arg1;
+ onUnlockUser(userId);
+ return true;
}
return false;
}
@@ -3911,6 +3919,52 @@
}
@Override
+ public IInputContentUriToken createInputContentUriToken(@Nullable IBinder token,
+ @Nullable Uri contentUri, @Nullable String packageName) {
+ if (!calledFromValidUser()) {
+ return null;
+ }
+
+ if (token == null) {
+ throw new NullPointerException("token");
+ }
+ if (packageName == null) {
+ throw new NullPointerException("packageName");
+ }
+ if (contentUri == null) {
+ throw new NullPointerException("contentUri");
+ }
+ final String contentUriScheme = contentUri.getScheme();
+ if (!"content".equals(contentUriScheme)) {
+ throw new InvalidParameterException("contentUri must have content scheme");
+ }
+
+ synchronized (mMethodMap) {
+ final int uid = Binder.getCallingUid();
+ if (mCurMethodId == null) {
+ return null;
+ }
+ if (mCurToken != token) {
+ Slog.e(TAG, "Ignoring createInputContentUriToken mCurToken=" + mCurToken
+ + " token=" + token);
+ return null;
+ }
+ // We cannot simply distinguish a bad IME that reports an arbitrary package name from
+ // an unfortunate IME whose internal state is already obsolete due to the asynchronous
+ // nature of our system. Let's compare it with our internal record.
+ if (!TextUtils.equals(mCurAttribute.packageName, packageName)) {
+ Slog.e(TAG, "Ignoring createInputContentUriToken mCurAttribute.packageName="
+ + mCurAttribute.packageName + " packageName=" + packageName);
+ return null;
+ }
+ final int imeUserId = UserHandle.getUserId(uid);
+ final int appUserId = UserHandle.getUserId(mCurClient.uid);
+ return new InputContentUriTokenHandler(contentUri, uid, packageName, imeUserId,
+ appUserId);
+ }
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 36ec2eb..64ca2e3 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -150,7 +150,7 @@
// used internally for synchronization
private final Object mLock = new Object();
- // --- fields below are final after systemReady() ---
+ // --- fields below are final after systemRunning() ---
private LocationFudger mLocationFudger;
private GeofenceManager mGeofenceManager;
private PackageManager mPackageManager;
@@ -168,6 +168,7 @@
// --- fields below are protected by mLock ---
// Set of providers that are explicitly enabled
+ // Only used by passive, fused & test. Network & GPS are controlled separately, and not listed.
private final Set<String> mEnabledProviders = new HashSet<String>();
// Set of providers that are explicitly disabled
@@ -236,12 +237,12 @@
if (D) Log.d(TAG, "Constructed");
- // most startup is deferred until systemReady()
+ // most startup is deferred until systemRunning()
}
public void systemRunning() {
synchronized (mLock) {
- if (D) Log.d(TAG, "systemReady()");
+ if (D) Log.d(TAG, "systemRunning()");
// fetch package manager
mPackageManager = mContext.getPackageManager();
@@ -321,7 +322,11 @@
|| Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
updateUserProfiles(mCurrentUserId);
} else if (Intent.ACTION_SHUTDOWN.equals(action)) {
- shutdownComponents();
+ // shutdown only if UserId indicates whole system, not just one user
+ if(D) Log.d(TAG, "Shutdown received with UserId: " + getSendingUserId());
+ if (getSendingUserId() == UserHandle.USER_ALL) {
+ shutdownComponents();
+ }
}
}
}, UserHandle.ALL, intentFilter, null, mLocationHandler);
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 42e75c6..a91e205 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -69,6 +69,7 @@
import android.util.Slog;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.widget.ICheckCredentialProgressCallback;
import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse;
@@ -300,9 +301,12 @@
for (int i = 0; i < users.size(); i++) {
UserInfo user = users.get(i);
UserHandle userHandle = user.getUserHandle();
- if (!mUserManager.isUserUnlockingOrUnlocked(userHandle)) {
+ final boolean isSecure = mStorage.hasPassword(user.id) || mStorage.hasPattern(user.id);
+ if (isSecure && !mUserManager.isUserUnlockingOrUnlocked(userHandle)) {
if (!user.isManagedProfile()) {
- showEncryptionNotification(userHandle);
+ // When the user is locked, we communicate it loud-and-clear
+ // on the lockscreen; we only show a notification below for
+ // locked managed profiles.
} else {
UserInfo parent = mUserManager.getProfileParent(user.id);
if (parent != null &&
@@ -338,21 +342,6 @@
showEncryptionNotification(user, title, message, detail, intent);
}
- private void showEncryptionNotification(UserHandle user) {
- Resources r = mContext.getResources();
- CharSequence title = r.getText(
- com.android.internal.R.string.user_encrypted_title);
- CharSequence message = r.getText(
- com.android.internal.R.string.user_encrypted_message);
- CharSequence detail = r.getText(
- com.android.internal.R.string.user_encrypted_detail);
-
- PendingIntent intent = PendingIntent.getBroadcast(mContext, 0, ACTION_NULL,
- PendingIntent.FLAG_UPDATE_CURRENT);
-
- showEncryptionNotification(user, title, message, detail, intent);
- }
-
private void showEncryptionNotification(UserHandle user, CharSequence title, CharSequence message,
CharSequence detail, PendingIntent intent) {
if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier());
@@ -407,7 +396,9 @@
List<UserInfo> profiles = mUserManager.getProfiles(userId);
for (int i = 0; i < profiles.size(); i++) {
UserInfo profile = profiles.get(i);
- if (profile.isManagedProfile()) {
+ final boolean isSecure =
+ mStorage.hasPassword(profile.id) || mStorage.hasPattern(profile.id);
+ if (isSecure && profile.isManagedProfile()) {
UserHandle userHandle = profile.getUserHandle();
if (!mUserManager.isUserUnlockingOrUnlocked(userHandle) &&
!mUserManager.isQuietModeEnabled(userHandle)) {
@@ -580,6 +571,18 @@
Slog.e(TAG, "Invalid tied profile lock type: " + quality);
}
}
+ try {
+ final String alias = LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userInfo.id;
+ java.security.KeyStore keyStore =
+ java.security.KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(null);
+ if (keyStore.containsAlias(alias)) {
+ keyStore.deleteEntry(alias);
+ }
+ } catch (KeyStoreException | NoSuchAlgorithmException |
+ CertificateException | IOException e) {
+ Slog.e(TAG, "Unable to remove tied profile key", e);
+ }
}
} catch (RemoteException re) {
Slog.e(TAG, "Unable to migrate old data", re);
@@ -757,7 +760,7 @@
private void unlockChildProfile(int profileHandle) throws RemoteException {
try {
doVerifyPassword(getDecryptedPasswordForTiedProfile(profileHandle), false,
- 0 /* no challenge */, profileHandle);
+ 0 /* no challenge */, profileHandle, null /* progressCallback */);
} catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
| NoSuchAlgorithmException | NoSuchPaddingException
| InvalidAlgorithmParameterException | IllegalBlockSizeException
@@ -944,7 +947,7 @@
CredentialHash willStore
= new CredentialHash(enrolledHandle, CredentialHash.VERSION_GATEKEEPER);
setUserKeyProtection(userId, pattern,
- doVerifyPattern(pattern, willStore, true, 0, userId));
+ doVerifyPattern(pattern, willStore, true, 0, userId, null /* progressCallback */));
mStorage.writePatternHash(enrolledHandle, userId);
fixateNewestUserKeyAuth(userId);
onUserLockChanged(userId);
@@ -1004,7 +1007,8 @@
CredentialHash willStore
= new CredentialHash(enrolledHandle, CredentialHash.VERSION_GATEKEEPER);
setUserKeyProtection(userId, password,
- doVerifyPassword(password, willStore, true, 0, userId));
+ doVerifyPassword(password, willStore, true, 0, userId,
+ null /* progressCallback */));
mStorage.writePasswordHash(enrolledHandle, userId);
fixateNewestUserKeyAuth(userId);
onUserLockChanged(userId);
@@ -1022,37 +1026,38 @@
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
keyGenerator.init(new SecureRandom());
SecretKey secretKey = keyGenerator.generateKey();
-
java.security.KeyStore keyStore = java.security.KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
- keyStore.setEntry(
- LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userId,
- new java.security.KeyStore.SecretKeyEntry(secretKey),
- new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
- .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
- .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
- .build());
- keyStore.setEntry(
- LockPatternUtils.PROFILE_KEY_NAME_DECRYPT + userId,
- new java.security.KeyStore.SecretKeyEntry(secretKey),
- new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
- .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
- .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
- .setUserAuthenticationRequired(true)
- .setUserAuthenticationValidityDurationSeconds(30)
- .build());
-
- // Key imported, obtain a reference to it.
- SecretKey keyStoreEncryptionKey = (SecretKey) keyStore.getKey(
- LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userId, null);
- // The original key can now be discarded.
-
- Cipher cipher = Cipher.getInstance(
- KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/"
- + KeyProperties.ENCRYPTION_PADDING_NONE);
- cipher.init(Cipher.ENCRYPT_MODE, keyStoreEncryptionKey);
- encryptionResult = cipher.doFinal(randomLockSeed);
- iv = cipher.getIV();
+ try {
+ keyStore.setEntry(
+ LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userId,
+ new java.security.KeyStore.SecretKeyEntry(secretKey),
+ new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .build());
+ keyStore.setEntry(
+ LockPatternUtils.PROFILE_KEY_NAME_DECRYPT + userId,
+ new java.security.KeyStore.SecretKeyEntry(secretKey),
+ new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .setUserAuthenticationRequired(true)
+ .setUserAuthenticationValidityDurationSeconds(30)
+ .build());
+ // Key imported, obtain a reference to it.
+ SecretKey keyStoreEncryptionKey = (SecretKey) keyStore.getKey(
+ LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userId, null);
+ Cipher cipher = Cipher.getInstance(
+ KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/"
+ + KeyProperties.ENCRYPTION_PADDING_NONE);
+ cipher.init(Cipher.ENCRYPT_MODE, keyStoreEncryptionKey);
+ encryptionResult = cipher.doFinal(randomLockSeed);
+ iv = cipher.getIV();
+ } finally {
+ // The original key can now be discarded.
+ keyStore.deleteEntry(LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userId);
+ }
} catch (CertificateException | UnrecoverableKeyException
| IOException | BadPaddingException | IllegalBlockSizeException | KeyStoreException
| NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
@@ -1202,28 +1207,36 @@
}
@Override
- public VerifyCredentialResponse checkPattern(String pattern, int userId) throws RemoteException {
- return doVerifyPattern(pattern, false, 0, userId);
+ public VerifyCredentialResponse checkPattern(String pattern, int userId,
+ ICheckCredentialProgressCallback progressCallback) throws RemoteException {
+ return doVerifyPattern(pattern, false, 0, userId, progressCallback);
}
@Override
public VerifyCredentialResponse verifyPattern(String pattern, long challenge, int userId)
throws RemoteException {
- return doVerifyPattern(pattern, true, challenge, userId);
+ return doVerifyPattern(pattern, true, challenge, userId, null /* progressCallback */);
}
private VerifyCredentialResponse doVerifyPattern(String pattern, boolean hasChallenge,
- long challenge, int userId) throws RemoteException {
+ long challenge, int userId, ICheckCredentialProgressCallback progressCallback)
+ throws RemoteException {
checkPasswordReadPermission(userId);
if (TextUtils.isEmpty(pattern)) {
throw new IllegalArgumentException("Pattern can't be null or empty");
}
CredentialHash storedHash = mStorage.readPatternHash(userId);
- return doVerifyPattern(pattern, storedHash, hasChallenge, challenge, userId);
+ return doVerifyPattern(pattern, storedHash, hasChallenge, challenge, userId,
+ progressCallback);
}
private VerifyCredentialResponse doVerifyPattern(String pattern, CredentialHash storedHash,
- boolean hasChallenge, long challenge, int userId) throws RemoteException {
+ boolean hasChallenge, long challenge, int userId,
+ ICheckCredentialProgressCallback progressCallback) throws RemoteException {
+
+ if (TextUtils.isEmpty(pattern)) {
+ throw new IllegalArgumentException("Pattern can't be null or empty");
+ }
boolean shouldReEnrollBaseZero = storedHash != null && storedHash.isBaseZeroPattern;
String patternToVerify;
@@ -1252,7 +1265,8 @@
public String adjustForKeystore(String pattern) {
return LockPatternUtils.patternStringToBaseZero(pattern);
}
- }
+ },
+ progressCallback
);
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK
@@ -1264,15 +1278,15 @@
}
@Override
- public VerifyCredentialResponse checkPassword(String password, int userId)
- throws RemoteException {
- return doVerifyPassword(password, false, 0, userId);
+ public VerifyCredentialResponse checkPassword(String password, int userId,
+ ICheckCredentialProgressCallback progressCallback) throws RemoteException {
+ return doVerifyPassword(password, false, 0, userId, progressCallback);
}
@Override
public VerifyCredentialResponse verifyPassword(String password, long challenge, int userId)
throws RemoteException {
- return doVerifyPassword(password, true, challenge, userId);
+ return doVerifyPassword(password, true, challenge, userId, null /* progressCallback */);
}
@Override
@@ -1285,8 +1299,10 @@
final int parentProfileId = mUserManager.getProfileParent(userId).id;
// Unlock parent by using parent's challenge
final VerifyCredentialResponse parentResponse = isPattern
- ? doVerifyPattern(password, true, challenge, parentProfileId)
- : doVerifyPassword(password, true, challenge, parentProfileId);
+ ? doVerifyPattern(password, true, challenge, parentProfileId,
+ null /* progressCallback */)
+ : doVerifyPassword(password, true, challenge, parentProfileId,
+ null /* progressCallback */);
if (parentResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
// Failed, just return parent's response
return parentResponse;
@@ -1296,7 +1312,7 @@
// Unlock work profile, and work profile with unified lock must use password only
return doVerifyPassword(getDecryptedPasswordForTiedProfile(userId), true,
challenge,
- userId);
+ userId, null /* progressCallback */);
} catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
| NoSuchAlgorithmException | NoSuchPaddingException
| InvalidAlgorithmParameterException | IllegalBlockSizeException
@@ -1307,17 +1323,23 @@
}
private VerifyCredentialResponse doVerifyPassword(String password, boolean hasChallenge,
- long challenge, int userId) throws RemoteException {
+ long challenge, int userId, ICheckCredentialProgressCallback progressCallback)
+ throws RemoteException {
checkPasswordReadPermission(userId);
if (TextUtils.isEmpty(password)) {
throw new IllegalArgumentException("Password can't be null or empty");
}
CredentialHash storedHash = mStorage.readPasswordHash(userId);
- return doVerifyPassword(password, storedHash, hasChallenge, challenge, userId);
+ return doVerifyPassword(password, storedHash, hasChallenge, challenge, userId,
+ progressCallback);
}
private VerifyCredentialResponse doVerifyPassword(String password, CredentialHash storedHash,
- boolean hasChallenge, long challenge, int userId) throws RemoteException {
+ boolean hasChallenge, long challenge, int userId,
+ ICheckCredentialProgressCallback progressCallback) throws RemoteException {
+ if (TextUtils.isEmpty(password)) {
+ throw new IllegalArgumentException("Password can't be null or empty");
+ }
return verifyCredential(userId, storedHash, password, hasChallenge, challenge,
new CredentialUtil() {
@Override
@@ -1335,12 +1357,12 @@
public String adjustForKeystore(String password) {
return password;
}
- }
- );
+ }, progressCallback);
}
private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash,
- String credential, boolean hasChallenge, long challenge, CredentialUtil credentialUtil)
+ String credential, boolean hasChallenge, long challenge, CredentialUtil credentialUtil,
+ ICheckCredentialProgressCallback progressCallback)
throws RemoteException {
if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(credential)) {
// don't need to pass empty credentials to GateKeeper
@@ -1397,7 +1419,13 @@
}
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
+
+
// credential has matched
+
+ if (progressCallback != null) {
+ progressCallback.onCredentialVerified();
+ }
unlockKeystore(credential, userId);
Slog.i(TAG, "Unlocking user " + userId +
@@ -1453,7 +1481,7 @@
try {
if (mLockPatternUtils.isLockPatternEnabled(userId)) {
- if (checkPattern(password, userId).getResponseCode()
+ if (checkPattern(password, userId, null /* progressCallback */).getResponseCode()
== GateKeeperResponse.RESPONSE_OK) {
return true;
}
@@ -1463,7 +1491,7 @@
try {
if (mLockPatternUtils.isLockPasswordEnabled(userId)) {
- if (checkPassword(password, userId).getResponseCode()
+ if (checkPassword(password, userId, null /* progressCallback */).getResponseCode()
== GateKeeperResponse.RESPONSE_OK) {
return true;
}
diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java
index 1653db9..6f8edec 100644
--- a/services/core/java/com/android/server/MasterClearReceiver.java
+++ b/services/core/java/com/android/server/MasterClearReceiver.java
@@ -47,6 +47,7 @@
final String reason = intent.getStringExtra(Intent.EXTRA_REASON);
final boolean wipeExternalStorage = intent.getBooleanExtra(
Intent.EXTRA_WIPE_EXTERNAL_STORAGE, false);
+ final boolean forceWipe = intent.getBooleanExtra(Intent.EXTRA_FORCE_MASTER_CLEAR, false);
Slog.w(TAG, "!!! FACTORY RESET !!!");
// The reboot call is blocking, so we need to do it on another thread.
@@ -54,7 +55,7 @@
@Override
public void run() {
try {
- RecoverySystem.rebootWipeUserData(context, shutdown, reason);
+ RecoverySystem.rebootWipeUserData(context, shutdown, reason, forceWipe);
Log.wtf(TAG, "Still running after master clear?!");
} catch (IOException e) {
Slog.e(TAG, "Can't perform master clear/factory reset", e);
diff --git a/services/core/java/com/android/server/NativeDaemonConnectorException.java b/services/core/java/com/android/server/NativeDaemonConnectorException.java
index 590bbcc..4d8881c 100644
--- a/services/core/java/com/android/server/NativeDaemonConnectorException.java
+++ b/services/core/java/com/android/server/NativeDaemonConnectorException.java
@@ -41,7 +41,7 @@
}
public int getCode() {
- return mEvent.getCode();
+ return mEvent != null ? mEvent.getCode() : -1;
}
public String getCmd() {
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index f236877..bdbd066 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -494,7 +494,7 @@
if (mLastPowerStateFromWifi != powerState) {
mLastPowerStateFromWifi = powerState;
try {
- getBatteryStats().noteWifiRadioPowerState(powerState, tsNanos);
+ getBatteryStats().noteWifiRadioPowerState(powerState, tsNanos, uid);
} catch (RemoteException e) {
}
}
@@ -971,6 +971,17 @@
//
// INetworkManagementService members
//
+ @Override
+ public INetd getNetdService() throws RemoteException {
+ final CountDownLatch connectedSignal = mConnectedSignal;
+ if (connectedSignal != null) {
+ try {
+ connectedSignal.await();
+ } catch (InterruptedException ignored) {}
+ }
+
+ return mNetdService;
+ }
@Override
public String[] listInterfaces() {
@@ -1299,8 +1310,9 @@
mConnector.execute("tether", "interface", "remove", iface);
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
+ } finally {
+ removeInterfaceFromLocalNetwork(iface);
}
- removeInterfaceFromLocalNetwork(iface);
}
@Override
@@ -2763,4 +2775,19 @@
public void removeInterfaceFromLocalNetwork(String iface) {
modifyInterfaceInNetwork("remove", "local", iface);
}
+
+ @Override
+ public int removeRoutesFromLocalNetwork(List<RouteInfo> routes) {
+ int failures = 0;
+
+ for (RouteInfo route : routes) {
+ try {
+ modifyRoute("remove", "local", route);
+ } catch (IllegalStateException e) {
+ failures++;
+ }
+ }
+
+ return failures;
+ }
}
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 680547a..080b46c 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -26,6 +26,7 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.service.persistentdata.IPersistentDataBlockService;
import android.service.persistentdata.PersistentDataBlockManager;
import android.util.Slog;
@@ -155,6 +156,14 @@
"Only the Admin user is allowed to change OEM unlock state");
}
}
+
+ private void enforceUserRestriction(String userRestriction) {
+ if (UserManager.get(mContext).hasUserRestriction(userRestriction)) {
+ throw new SecurityException(
+ "OEM unlock is disallowed by user restriction: " + userRestriction);
+ }
+ }
+
private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
// skip over checksum
inputStream.skipBytes(DIGEST_SIZE_BYTES);
@@ -447,14 +456,20 @@
}
@Override
- public void setOemUnlockEnabled(boolean enabled) {
+ public void setOemUnlockEnabled(boolean enabled) throws SecurityException {
// do not allow monkey to flip the flag
if (ActivityManager.isUserAMonkey()) {
return;
}
+
enforceOemUnlockWritePermission();
enforceIsAdmin();
+ if (enabled) {
+ // Do not allow oem unlock to be enabled if it's disallowed by a user restriction.
+ enforceUserRestriction(UserManager.DISALLOW_OEM_UNLOCK);
+ enforceUserRestriction(UserManager.DISALLOW_FACTORY_RESET);
+ }
synchronized (mLock) {
doSetOemUnlockEnabledLocked(enabled);
computeAndWriteDigestLocked();
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index d48aeed..356ccb3 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -17,109 +17,372 @@
package com.android.server;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.util.EventLog;
-import android.util.Slog;
+import android.content.pm.ResolveInfo;
import android.os.Binder;
import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.provider.MediaStore;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructStat;
+import android.util.Slog;
-import java.util.ArrayList;
+import com.android.internal.app.ResolverActivity;
+import com.android.internal.os.BackgroundThread;
+
+import dalvik.system.DexFile;
+import dalvik.system.VMRuntime;
+
import java.io.FileDescriptor;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.ArrayList;
/**
* <p>PinnerService pins important files for key processes in memory.</p>
* <p>Files to pin are specified in the config_defaultPinnerServiceFiles
- * overlay. </p>
+ * overlay.</p>
+ * <p>Pin the default camera application if specified in config_pinnerCameraApp.</p>
*/
public final class PinnerService extends SystemService {
private static final boolean DEBUG = false;
private static final String TAG = "PinnerService";
private final Context mContext;
- private final ArrayList<String> mPinnedFiles = new ArrayList<String>();
+ private final ArrayList<PinnedFile> mPinnedFiles = new ArrayList<PinnedFile>();
+ private final ArrayList<PinnedFile> mPinnedCameraFiles = new ArrayList<PinnedFile>();
+ private final boolean mShouldPinCamera;
private BinderService mBinderService;
+ private final long MAX_CAMERA_PIN_SIZE = 50 * (1 << 20); //50MB max
+
+ private PinnerHandler mPinnerHandler = null;
+
public PinnerService(Context context) {
super(context);
mContext = context;
-
+ mShouldPinCamera = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_pinnerCameraApp);
+ mPinnerHandler = new PinnerHandler(BackgroundThread.get().getLooper());
}
@Override
public void onStart() {
- Slog.e(TAG, "Starting PinnerService");
-
+ if (DEBUG) {
+ Slog.i(TAG, "Starting PinnerService");
+ }
mBinderService = new BinderService();
publishBinderService("pinner", mBinderService);
- // Files to pin come from the overlay and can be specified per-device config
- // Continue trying to pin remaining files even if there is a failure
- String[] filesToPin = mContext.getResources().getStringArray(com.android.internal.R.array.config_defaultPinnerServiceFiles);
- for (int i = 0; i < filesToPin.length; i++){
- boolean success = pinFile(filesToPin[i], 0, 0);
- if (success == true) {
- mPinnedFiles.add(filesToPin[i]);
- Slog.i(TAG, "Pinned file = " + filesToPin[i]);
- } else {
- Slog.e(TAG, "Failed to pin file = " + filesToPin[i]);
+ mPinnerHandler.obtainMessage(PinnerHandler.PIN_ONSTART_MSG).sendToTarget();
+ mPinnerHandler.obtainMessage(PinnerHandler.PIN_CAMERA_MSG, UserHandle.USER_SYSTEM, 0)
+ .sendToTarget();
+ }
+
+ /**
+ * Pin camera on user switch.
+ * If more than one user is using the device
+ * each user may set a different preference for the camera app.
+ * Make sure that user's preference is pinned into memory.
+ */
+ @Override
+ public void onSwitchUser(int userHandle) {
+ mPinnerHandler.obtainMessage(PinnerHandler.PIN_CAMERA_MSG, userHandle, 0).sendToTarget();
+ }
+
+ /**
+ * Handler for on start pinning message
+ */
+ private void handlePinOnStart() {
+ // Files to pin come from the overlay and can be specified per-device config
+ String[] filesToPin = mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_defaultPinnerServiceFiles);
+ synchronized(this) {
+ // Continue trying to pin remaining files even if there is a failure
+ for (int i = 0; i < filesToPin.length; i++){
+ PinnedFile pf = pinFile(filesToPin[i], 0, 0, 0);
+ if (pf != null) {
+ mPinnedFiles.add(pf);
+ if (DEBUG) {
+ Slog.i(TAG, "Pinned file = " + pf.mFilename);
+ }
+ } else {
+ Slog.e(TAG, "Failed to pin file = " + filesToPin[i]);
+ }
}
}
}
- // mlock length bytes of fileToPin in memory, starting at offset
- // length == 0 means pin from offset to end of file
- private boolean pinFile(String fileToPin, long offset, long length) {
+ /**
+ * Handler for camera pinning message
+ */
+ private void handlePinCamera(int userHandle) {
+ if (mShouldPinCamera) {
+ synchronized(this) {
+ boolean success = pinCamera(userHandle);
+ if (!success) {
+ //this is not necessarily an error
+ if (DEBUG) {
+ Slog.v(TAG, "Failed to pin camera.");
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * determine if the camera app is already pinned by comparing the
+ * intent resolution to the pinned files list
+ */
+ private boolean alreadyPinned(int userHandle) {
+ ApplicationInfo cameraInfo = getCameraInfo(userHandle);
+ if (cameraInfo == null ) {
+ return false;
+ }
+ for (int i = 0; i < mPinnedCameraFiles.size(); i++) {
+ if (mPinnedCameraFiles.get(i).mFilename.equals(cameraInfo.sourceDir)) {
+ if (DEBUG) {
+ Slog.v(TAG, "Camera is already pinned");
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void unpinCameraApp() {
+ for (int i = 0; i < mPinnedCameraFiles.size(); i++) {
+ unpinFile(mPinnedCameraFiles.get(i));
+ }
+ mPinnedCameraFiles.clear();
+ }
+
+ private boolean isResolverActivity(ActivityInfo info) {
+ return ResolverActivity.class.getName().equals(info.name);
+ }
+
+ private ApplicationInfo getCameraInfo(int userHandle) {
+ // find the camera via an intent
+ // use INTENT_ACTION_STILL_IMAGE_CAMERA instead of _SECURE. On a
+ // device without a fbe enabled, the _SECURE intent will never get set.
+ Intent cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
+ PackageManager pm = mContext.getPackageManager();
+ ResolveInfo cameraResolveInfo = pm.resolveActivityAsUser(cameraIntent,
+ PackageManager.MATCH_DEFAULT_ONLY | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ userHandle);
+ if (cameraResolveInfo == null ) {
+ //this is not necessarily an error
+ if (DEBUG) {
+ Slog.v(TAG, "Unable to resolve camera intent");
+ }
+ return null;
+ }
+
+ if (isResolverActivity(cameraResolveInfo.activityInfo))
+ {
+ return null;
+ }
+
+ return cameraResolveInfo.activityInfo.applicationInfo;
+ }
+
+ private boolean pinCamera(int userHandle){
+ //we may have already pinned a camera app. If we've pinned this
+ //camera app, we're done. otherwise, unpin and pin the new app
+ if (alreadyPinned(userHandle)){
+ return true;
+ }
+
+ ApplicationInfo cameraInfo = getCameraInfo(userHandle);
+ if (cameraInfo == null) {
+ return false;
+ }
+
+ //unpin after checking that the camera intent has resolved
+ //this prevents us from thrashing when switching users with
+ //FBE enabled, because the intent won't resolve until the unlock
+ unpinCameraApp();
+
+ //pin APK
+ String camAPK = cameraInfo.sourceDir;
+ PinnedFile pf = pinFile(camAPK, 0, 0, MAX_CAMERA_PIN_SIZE);
+ if (pf == null) {
+ Slog.e(TAG, "Failed to pin " + camAPK);
+ return false;
+ }
+ if (DEBUG) {
+ Slog.i(TAG, "Pinned " + pf.mFilename);
+ }
+ mPinnedCameraFiles.add(pf);
+
+ // determine the ABI from either ApplicationInfo or Build
+ String arch = "arm";
+ if (cameraInfo.primaryCpuAbi != null
+ && VMRuntime.is64BitAbi(cameraInfo.primaryCpuAbi)) {
+ arch = arch + "64";
+ } else {
+ if (VMRuntime.is64BitAbi(Build.SUPPORTED_ABIS[0])) {
+ arch = arch + "64";
+ }
+ }
+
+ // get the path to the odex or oat file
+ String baseCodePath = cameraInfo.getBaseCodePath();
+ String odex = null;
+ try {
+ odex = DexFile.getDexFileOutputPath(baseCodePath, arch);
+ } catch (IOException ioe) {}
+ if (odex == null) {
+ return true;
+ }
+
+ //not pinning the oat/odex is not a fatal error
+ pf = pinFile(odex, 0, 0, MAX_CAMERA_PIN_SIZE);
+ if (pf != null) {
+ mPinnedCameraFiles.add(pf);
+ if (DEBUG) {
+ Slog.i(TAG, "Pinned " + pf.mFilename);
+ }
+ }
+
+ return true;
+ }
+
+
+ /** mlock length bytes of fileToPin in memory, starting at offset
+ * length == 0 means pin from offset to end of file
+ * maxSize == 0 means infinite
+ */
+ private static PinnedFile pinFile(String fileToPin, long offset, long length, long maxSize) {
FileDescriptor fd = new FileDescriptor();
try {
- fd = Os.open(fileToPin, OsConstants.O_RDONLY | OsConstants.O_CLOEXEC | OsConstants.O_NOFOLLOW, OsConstants.O_RDONLY);
+ fd = Os.open(fileToPin,
+ OsConstants.O_RDONLY | OsConstants.O_CLOEXEC | OsConstants.O_NOFOLLOW,
+ OsConstants.O_RDONLY);
StructStat sb = Os.fstat(fd);
if (offset + length > sb.st_size) {
Os.close(fd);
- return false;
+ Slog.e(TAG, "Failed to pin file " + fileToPin +
+ ", request extends beyond end of file. offset + length = "
+ + (offset + length) + ", file length = " + sb.st_size);
+ return null;
}
if (length == 0) {
length = sb.st_size - offset;
}
- long address = Os.mmap(0, length, OsConstants.PROT_READ, OsConstants.MAP_PRIVATE, fd, offset);
+ if (maxSize > 0 && length > maxSize) {
+ Slog.e(TAG, "Could not pin file " + fileToPin +
+ ", size = " + length + ", maxSize = " + maxSize);
+ Os.close(fd);
+ return null;
+ }
+
+ long address = Os.mmap(0, length, OsConstants.PROT_READ,
+ OsConstants.MAP_PRIVATE, fd, offset);
Os.close(fd);
Os.mlock(address, length);
- return true;
+ return new PinnedFile(address, length, fileToPin);
} catch (ErrnoException e) {
- Slog.e(TAG, "Failed to pin file " + fileToPin + " with error " + e.getMessage());
+ Slog.e(TAG, "Could not pin file " + fileToPin + " with error " + e.getMessage());
if(fd.valid()) {
- try { Os.close(fd); }
- catch (ErrnoException eClose) {Slog.e(TAG, "Failed to close fd, error = " + eClose.getMessage());}
+ try {
+ Os.close(fd);
+ }
+ catch (ErrnoException eClose) {
+ Slog.e(TAG, "Failed to close fd, error = " + eClose.getMessage());
+ }
}
- return false;
+ return null;
}
}
+ private static boolean unpinFile(PinnedFile pf) {
+ try {
+ Os.munlock(pf.mAddress, pf.mLength);
+ } catch (ErrnoException e) {
+ Slog.e(TAG, "Failed to unpin file " + pf.mFilename + " with error " + e.getMessage());
+ return false;
+ }
+ if (DEBUG) {
+ Slog.i(TAG, "Unpinned file " + pf.mFilename );
+ }
+ return true;
+ }
private final class BinderService extends Binder {
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
pw.println("Pinned Files:");
- for (int i = 0; i < mPinnedFiles.size(); i++) {
- pw.println(mPinnedFiles.get(i));
+ synchronized(this) {
+ for (int i = 0; i < mPinnedFiles.size(); i++) {
+ pw.println(mPinnedFiles.get(i).mFilename);
+ }
+ for (int i = 0; i < mPinnedCameraFiles.size(); i++) {
+ pw.println(mPinnedCameraFiles.get(i).mFilename);
+ }
}
}
}
+
+ private static class PinnedFile {
+ long mAddress;
+ long mLength;
+ String mFilename;
+
+ PinnedFile(long address, long length, String filename) {
+ mAddress = address;
+ mLength = length;
+ mFilename = filename;
+ }
+ }
+
+ final class PinnerHandler extends Handler {
+ static final int PIN_CAMERA_MSG = 4000;
+ static final int PIN_ONSTART_MSG = 4001;
+
+ public PinnerHandler(Looper looper) {
+ super(looper, null, true);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+
+ case PIN_CAMERA_MSG:
+ {
+ handlePinCamera(msg.arg1);
+ }
+ break;
+
+ case PIN_ONSTART_MSG:
+ {
+ handlePinOnStart();
+ }
+ break;
+
+ default:
+ super.handleMessage(msg);
+ }
+ }
+ }
+
}
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index ecc69e9..90f507c 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -156,12 +156,15 @@
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "onStartUser "
+ + service.getClass().getName());
try {
service.onStartUser(userHandle);
} catch (Exception ex) {
Slog.wtf(TAG, "Failure reporting start of user " + userHandle
+ " to service " + service.getClass().getName(), ex);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
@@ -169,12 +172,15 @@
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "onUnlockUser "
+ + service.getClass().getName());
try {
service.onUnlockUser(userHandle);
} catch (Exception ex) {
Slog.wtf(TAG, "Failure reporting unlock of user " + userHandle
+ " to service " + service.getClass().getName(), ex);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
@@ -182,12 +188,15 @@
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "onSwitchUser "
+ + service.getClass().getName());
try {
service.onSwitchUser(userHandle);
} catch (Exception ex) {
Slog.wtf(TAG, "Failure reporting switch of user " + userHandle
+ " to service " + service.getClass().getName(), ex);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
@@ -195,12 +204,15 @@
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "onStopUser "
+ + service.getClass().getName());
try {
service.onStopUser(userHandle);
} catch (Exception ex) {
Slog.wtf(TAG, "Failure reporting stop of user " + userHandle
+ " to service " + service.getClass().getName(), ex);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
@@ -208,12 +220,15 @@
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "onCleanupUser "
+ + service.getClass().getName());
try {
service.onCleanupUser(userHandle);
} catch (Exception ex) {
Slog.wtf(TAG, "Failure reporting cleanup of user " + userHandle
+ " to service " + service.getClass().getName(), ex);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
diff --git a/services/core/java/com/android/server/TwilightCalculator.java b/services/core/java/com/android/server/TwilightCalculator.java
deleted file mode 100644
index 5839b16..0000000
--- a/services/core/java/com/android/server/TwilightCalculator.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.text.format.DateUtils;
-
-/** @hide */
-public class TwilightCalculator {
-
- /** Value of {@link #mState} if it is currently day */
- public static final int DAY = 0;
-
- /** Value of {@link #mState} if it is currently night */
- public static final int NIGHT = 1;
-
- private static final float DEGREES_TO_RADIANS = (float) (Math.PI / 180.0f);
-
- // element for calculating solar transit.
- private static final float J0 = 0.0009f;
-
- // correction for civil twilight
- private static final float ALTIDUTE_CORRECTION_CIVIL_TWILIGHT = -0.104719755f;
-
- // coefficients for calculating Equation of Center.
- private static final float C1 = 0.0334196f;
- private static final float C2 = 0.000349066f;
- private static final float C3 = 0.000005236f;
-
- private static final float OBLIQUITY = 0.40927971f;
-
- // Java time on Jan 1, 2000 12:00 UTC.
- private static final long UTC_2000 = 946728000000L;
-
- /**
- * Time of sunset (civil twilight) in milliseconds or -1 in the case the day
- * or night never ends.
- */
- public long mSunset;
-
- /**
- * Time of sunrise (civil twilight) in milliseconds or -1 in the case the
- * day or night never ends.
- */
- public long mSunrise;
-
- /** Current state */
- public int mState;
-
- /**
- * calculates the civil twilight bases on time and geo-coordinates.
- *
- * @param time time in milliseconds.
- * @param latiude latitude in degrees.
- * @param longitude latitude in degrees.
- */
- public void calculateTwilight(long time, double latiude, double longitude) {
- final float daysSince2000 = (float) (time - UTC_2000) / DateUtils.DAY_IN_MILLIS;
-
- // mean anomaly
- final float meanAnomaly = 6.240059968f + daysSince2000 * 0.01720197f;
-
- // true anomaly
- final double trueAnomaly = meanAnomaly + C1 * Math.sin(meanAnomaly) + C2
- * Math.sin(2 * meanAnomaly) + C3 * Math.sin(3 * meanAnomaly);
-
- // ecliptic longitude
- final double solarLng = trueAnomaly + 1.796593063d + Math.PI;
-
- // solar transit in days since 2000
- final double arcLongitude = -longitude / 360;
- float n = Math.round(daysSince2000 - J0 - arcLongitude);
- double solarTransitJ2000 = n + J0 + arcLongitude + 0.0053d * Math.sin(meanAnomaly)
- + -0.0069d * Math.sin(2 * solarLng);
-
- // declination of sun
- double solarDec = Math.asin(Math.sin(solarLng) * Math.sin(OBLIQUITY));
-
- final double latRad = latiude * DEGREES_TO_RADIANS;
-
- double cosHourAngle = (Math.sin(ALTIDUTE_CORRECTION_CIVIL_TWILIGHT) - Math.sin(latRad)
- * Math.sin(solarDec)) / (Math.cos(latRad) * Math.cos(solarDec));
- // The day or night never ends for the given date and location, if this value is out of
- // range.
- if (cosHourAngle >= 1) {
- mState = NIGHT;
- mSunset = -1;
- mSunrise = -1;
- return;
- } else if (cosHourAngle <= -1) {
- mState = DAY;
- mSunset = -1;
- mSunrise = -1;
- return;
- }
-
- float hourAngle = (float) (Math.acos(cosHourAngle) / (2 * Math.PI));
-
- mSunset = Math.round((solarTransitJ2000 + hourAngle) * DateUtils.DAY_IN_MILLIS) + UTC_2000;
- mSunrise = Math.round((solarTransitJ2000 - hourAngle) * DateUtils.DAY_IN_MILLIS) + UTC_2000;
-
- if (mSunrise < time && mSunset > time) {
- mState = DAY;
- } else {
- mState = NIGHT;
- }
- }
-
-}
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 6f713cd..bb5f62b 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
@@ -155,8 +156,13 @@
private final TwilightListener mTwilightListener = new TwilightListener() {
@Override
- public void onTwilightStateChanged() {
- updateTwilight();
+ public void onTwilightStateChanged(@Nullable TwilightState state) {
+ synchronized (mLock) {
+ if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+ updateComputedNightModeLocked();
+ updateLocked(0, 0);
+ }
+ }
}
};
@@ -344,8 +350,8 @@
pw.print(" mSystemReady="); pw.println(mSystemReady);
if (mTwilightManager != null) {
// We may not have a TwilightManager.
- pw.print(" mTwilightService.getCurrentState()=");
- pw.println(mTwilightManager.getCurrentState());
+ pw.print(" mTwilightService.getLastTwilightState()=");
+ pw.println(mTwilightManager.getLastTwilightState());
}
}
}
@@ -355,9 +361,6 @@
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
synchronized (mLock) {
mTwilightManager = getLocalService(TwilightManager.class);
- if (mTwilightManager != null) {
- mTwilightManager.registerListener(mTwilightListener, mHandler);
- }
mSystemReady = true;
mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
updateComputedNightModeLocked();
@@ -411,10 +414,16 @@
}
if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+ if (mTwilightManager != null) {
+ mTwilightManager.registerListener(mTwilightListener, mHandler);
+ }
updateComputedNightModeLocked();
uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
: Configuration.UI_MODE_NIGHT_NO;
} else {
+ if (mTwilightManager != null) {
+ mTwilightManager.unregisterListener(mTwilightListener);
+ }
uiMode |= mNightMode << 4;
}
@@ -668,18 +677,9 @@
}
}
- void updateTwilight() {
- synchronized (mLock) {
- if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
- updateComputedNightModeLocked();
- updateLocked(0, 0);
- }
- }
- }
-
private void updateComputedNightModeLocked() {
if (mTwilightManager != null) {
- TwilightState state = mTwilightManager.getCurrentState();
+ TwilightState state = mTwilightManager.getLastTwilightState();
if (state != null) {
mComputedNightMode = state.isNight();
}
diff --git a/services/core/java/com/android/server/UiThread.java b/services/core/java/com/android/server/UiThread.java
index c06afc2..1bc6250 100644
--- a/services/core/java/com/android/server/UiThread.java
+++ b/services/core/java/com/android/server/UiThread.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.os.Handler;
+import android.os.Process;
import android.os.Trace;
/**
@@ -29,7 +30,9 @@
private static Handler sHandler;
private UiThread() {
- super("android.ui", android.os.Process.THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
+ super("android.ui", Process.THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
+ // Make sure UiThread is in the fg stune boost group
+ Process.setThreadGroup(Process.myTid(), Process.THREAD_GROUP_TOP_APP);
}
private static void ensureThreadLocked() {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 39f054c..c9bbb76 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -934,6 +934,7 @@
* Should only be called inside of a clearCallingIdentity block.
*/
private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) {
+ mAuthenticatorCache.updateServices(userId);
Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
AuthenticatorDescription[] types =
@@ -947,8 +948,6 @@
return types;
}
-
-
private boolean isCrossUser(int callingUid, int userId) {
return (userId != UserHandle.getCallingUserId()
&& callingUid != Process.myUid()
@@ -1225,11 +1224,13 @@
} finally {
db.endTransaction();
}
- sendAccountsChangedBroadcast(accounts.userId);
}
if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) {
addAccountToLinkedRestrictedUsers(account, accounts.userId);
}
+
+ // Only send LOGIN_ACCOUNTS_CHANGED when the database changed.
+ sendAccountsChangedBroadcast(accounts.userId);
return true;
}
@@ -1412,7 +1413,6 @@
synchronized (accounts.cacheLock) {
final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
db.beginTransaction();
- boolean isSuccessful = false;
Account renamedAccount = new Account(newName, accountToRename.type);
try {
final long accountId = getAccountIdLocked(db, accountToRename);
@@ -1425,54 +1425,51 @@
values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
db.setTransactionSuccessful();
- isSuccessful = true;
logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_ACCOUNTS, accountId,
accounts);
}
} finally {
db.endTransaction();
- if (isSuccessful) {
- /*
- * Database transaction was successful. Clean up cached
- * data associated with the account in the user profile.
- */
- insertAccountIntoCacheLocked(accounts, renamedAccount);
- /*
- * Extract the data and token caches before removing the
- * old account to preserve the user data associated with
- * the account.
- */
- HashMap<String, String> tmpData = accounts.userDataCache.get(accountToRename);
- HashMap<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
- removeAccountFromCacheLocked(accounts, accountToRename);
- /*
- * Update the cached data associated with the renamed
- * account.
- */
- accounts.userDataCache.put(renamedAccount, tmpData);
- accounts.authTokenCache.put(renamedAccount, tmpTokens);
- accounts.previousNameCache.put(
- renamedAccount,
- new AtomicReference<String>(accountToRename.name));
- resultAccount = renamedAccount;
+ }
+ /*
+ * Database transaction was successful. Clean up cached
+ * data associated with the account in the user profile.
+ */
+ insertAccountIntoCacheLocked(accounts, renamedAccount);
+ /*
+ * Extract the data and token caches before removing the
+ * old account to preserve the user data associated with
+ * the account.
+ */
+ HashMap<String, String> tmpData = accounts.userDataCache.get(accountToRename);
+ HashMap<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
+ removeAccountFromCacheLocked(accounts, accountToRename);
+ /*
+ * Update the cached data associated with the renamed
+ * account.
+ */
+ accounts.userDataCache.put(renamedAccount, tmpData);
+ accounts.authTokenCache.put(renamedAccount, tmpTokens);
+ accounts.previousNameCache.put(
+ renamedAccount,
+ new AtomicReference<String>(accountToRename.name));
+ resultAccount = renamedAccount;
- int parentUserId = accounts.userId;
- if (canHaveProfile(parentUserId)) {
- /*
- * Owner or system user account was renamed, rename the account for
- * those users with which the account was shared.
- */
- List<UserInfo> users = getUserManager().getUsers(true);
- for (UserInfo user : users) {
- if (user.isRestricted()
- && (user.restrictedProfileParentId == parentUserId)) {
- renameSharedAccountAsUser(accountToRename, newName, user.id);
- }
- }
+ int parentUserId = accounts.userId;
+ if (canHaveProfile(parentUserId)) {
+ /*
+ * Owner or system user account was renamed, rename the account for
+ * those users with which the account was shared.
+ */
+ List<UserInfo> users = getUserManager().getUsers(true);
+ for (UserInfo user : users) {
+ if (user.isRestricted()
+ && (user.restrictedProfileParentId == parentUserId)) {
+ renameSharedAccountAsUser(accountToRename, newName, user.id);
}
- sendAccountsChangedBroadcast(accounts.userId);
}
}
+ sendAccountsChangedBroadcast(accounts.userId);
}
return resultAccount;
}
@@ -1667,7 +1664,7 @@
}
private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
- int deleted;
+ boolean isChanged = false;
boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
if (!userUnlocked) {
Slog.i(TAG, "Removing account " + account + " while user "+ accounts.userId
@@ -1677,25 +1674,38 @@
final SQLiteDatabase db = userUnlocked
? accounts.openHelper.getWritableDatabaseUserIsUnlocked()
: accounts.openHelper.getWritableDatabase();
- final long accountId = getAccountIdLocked(db, account);
db.beginTransaction();
+ // Set to a dummy value, this will only be used if the database
+ // transaction succeeds.
+ long accountId = -1;
try {
- deleted = db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
- + "=?", new String[]{account.name, account.type});
- if (userUnlocked) {
- // Delete from CE table
- deleted = db.delete(CE_TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
- + "=?", new String[]{account.name, account.type});
+ accountId = getAccountIdLocked(db, account);
+ if (accountId >= 0) {
+ db.delete(
+ TABLE_ACCOUNTS,
+ ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
+ new String[]{ account.name, account.type });
+ if (userUnlocked) {
+ // Delete from CE table
+ db.delete(
+ CE_TABLE_ACCOUNTS,
+ ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
+ new String[]{ account.name, account.type });
+ }
+ db.setTransactionSuccessful();
+ isChanged = true;
}
- db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
- removeAccountFromCacheLocked(accounts, account);
- sendAccountsChangedBroadcast(accounts.userId);
- String action = userUnlocked ? DebugDbHelper.ACTION_ACCOUNT_REMOVE
- : DebugDbHelper.ACTION_ACCOUNT_REMOVE_DE;
- logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts);
+ if (isChanged) {
+ removeAccountFromCacheLocked(accounts, account);
+ // Only broadcast LOGIN_ACCOUNTS_CHANGED if a change occured.
+ sendAccountsChangedBroadcast(accounts.userId);
+ String action = userUnlocked ? DebugDbHelper.ACTION_ACCOUNT_REMOVE
+ : DebugDbHelper.ACTION_ACCOUNT_REMOVE_DE;
+ logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts);
+ }
}
long id = Binder.clearCallingIdentity();
try {
@@ -1712,7 +1722,7 @@
} finally {
Binder.restoreCallingIdentity(id);
}
- return (deleted > 0);
+ return isChanged;
}
@Override
@@ -1936,6 +1946,7 @@
if (account == null) {
return;
}
+ boolean isChanged = false;
synchronized (accounts.cacheLock) {
final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
db.beginTransaction();
@@ -1945,12 +1956,17 @@
final long accountId = getAccountIdLocked(db, account);
if (accountId >= 0) {
final String[] argsAccountId = {String.valueOf(accountId)};
- db.update(CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
- db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
+ db.update(
+ CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
+ db.delete(
+ CE_TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
accounts.authTokenCache.remove(account);
accounts.accountTokenCaches.remove(account);
db.setTransactionSuccessful();
-
+ // If there is an account whose password will be updated and the database
+ // transactions succeed, then we say that a change has occured. Even if the
+ // new password is the same as the old and there were no authtokens to delete.
+ isChanged = true;
String action = (password == null || password.length() == 0) ?
DebugDbHelper.ACTION_CLEAR_PASSWORD
: DebugDbHelper.ACTION_SET_PASSWORD;
@@ -1958,8 +1974,11 @@
}
} finally {
db.endTransaction();
+ if (isChanged) {
+ // Send LOGIN_ACCOUNTS_CHANGED only if the something changed.
+ sendAccountsChangedBroadcast(accounts.userId);
+ }
}
- sendAccountsChangedBroadcast(accounts.userId);
}
}
diff --git a/services/core/java/com/android/server/accounts/IAccountAuthenticatorCache.java b/services/core/java/com/android/server/accounts/IAccountAuthenticatorCache.java
index bb09687..2c7d921 100644
--- a/services/core/java/com/android/server/accounts/IAccountAuthenticatorCache.java
+++ b/services/core/java/com/android/server/accounts/IAccountAuthenticatorCache.java
@@ -64,4 +64,10 @@
Handler handler);
void invalidateCache(int userId);
+
+ /**
+ * Request to update services info for which package has been updated, but hasn't been
+ * picked up by the cache.
+ */
+ void updateServices(int userId);
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index ee2fa51..1bcff1a 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -699,7 +699,7 @@
throw new IllegalArgumentException("null notification");
}
if (r.foregroundId != id) {
- r.cancelNotification();
+ cancelForegroudNotificationLocked(r);
r.foregroundId = id;
}
notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
@@ -721,7 +721,7 @@
}
}
if ((flags & Service.STOP_FOREGROUND_REMOVE) != 0) {
- r.cancelNotification();
+ cancelForegroudNotificationLocked(r);
r.foregroundId = 0;
r.foregroundNoti = null;
} else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
@@ -738,6 +738,27 @@
}
}
+ private void cancelForegroudNotificationLocked(ServiceRecord r) {
+ if (r.foregroundId != 0) {
+ // First check to see if this app has any other active foreground services
+ // with the same notification ID. If so, we shouldn't actually cancel it,
+ // because that would wipe away the notification that still needs to be shown
+ // due the other service.
+ ServiceMap sm = getServiceMap(r.userId);
+ if (sm != null) {
+ for (int i = sm.mServicesByName.size()-1; i >= 0; i--) {
+ ServiceRecord other = sm.mServicesByName.valueAt(i);
+ if (other != r && other.foregroundId == r.foregroundId
+ && other.packageName.equals(r.packageName)) {
+ // Found one! Abort the cancel.
+ return;
+ }
+ }
+ }
+ r.cancelNotification();
+ }
+ }
+
private void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) {
boolean anyForeground = false;
for (int i=proc.services.size()-1; i>=0; i--) {
@@ -1560,7 +1581,7 @@
r.makeRestarting(mAm.mProcessStats.getMemFactorLocked(), now);
}
- r.cancelNotification();
+ cancelForegroudNotificationLocked(r);
mAm.mHandler.removeCallbacks(r.restarter);
mAm.mHandler.postAtTime(r.restarter, r.nextRestartTime);
@@ -2022,7 +2043,7 @@
}
}
- r.cancelNotification();
+ cancelForegroudNotificationLocked(r);
r.isForeground = false;
r.foregroundId = 0;
r.foregroundNoti = null;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 391323e..9e601eb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -63,6 +63,8 @@
import org.xmlpull.v1.XmlSerializer;
import android.Manifest;
+import android.Manifest.permission;
+import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
@@ -139,7 +141,6 @@
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -568,6 +569,9 @@
private boolean mShowDialogs = true;
private boolean mInVrMode = false;
+ // Whether we should use SCHED_FIFO for UI and RenderThreads.
+ private boolean mUseFifoUiScheduling = false;
+
BroadcastQueue mFgBroadcastQueue;
BroadcastQueue mBgBroadcastQueue;
// Convenient for easy iteration over the queues. Foreground is first
@@ -640,30 +644,41 @@
return mShowDialogs && !mSleeping && !mShuttingDown;
}
- // it's a semaphore; boost when 0->1, reset when 1->0
- static ThreadLocal<Integer> sIsBoosted = new ThreadLocal<Integer>() {
- @Override protected Integer initialValue() {
- return 0;
+ private static final class PriorityState {
+ // Acts as counter for number of synchronized region that needs to acquire 'this' as a lock
+ // the current thread is currently in. When it drops down to zero, we will no longer boost
+ // the thread's priority.
+ private int regionCounter = 0;
+
+ // The thread's previous priority before boosting.
+ private int prevPriority = Integer.MIN_VALUE;
+ }
+
+ static ThreadLocal<PriorityState> sThreadPriorityState = new ThreadLocal<PriorityState>() {
+ @Override protected PriorityState initialValue() {
+ return new PriorityState();
}
};
static void boostPriorityForLockedSection() {
- if (sIsBoosted.get() == 0) {
- // boost to prio 118 while holding a global lock
- Process.setThreadPriority(Process.myTid(), -2);
- //Log.e(TAG, "PRIORITY BOOST: set priority on TID " + Process.myTid());
+ int tid = Process.myTid();
+ int prevPriority = Process.getThreadPriority(tid);
+ PriorityState state = sThreadPriorityState.get();
+ if (state.regionCounter == 0 && prevPriority > -2) {
+ state.prevPriority = prevPriority;
+ Process.setThreadPriority(tid, -2);
}
- int cur = sIsBoosted.get();
- sIsBoosted.set(cur + 1);
+ state.regionCounter++;
}
static void resetPriorityAfterLockedSection() {
- sIsBoosted.set(sIsBoosted.get() - 1);
- if (sIsBoosted.get() == 0) {
- //Log.e(TAG, "PRIORITY BOOST: reset priority on TID " + Process.myTid());
- Process.setThreadPriority(Process.myTid(), 0);
+ PriorityState state = sThreadPriorityState.get();
+ state.regionCounter--;
+ if (state.regionCounter == 0 && state.prevPriority > -2) {
+ Process.setThreadPriority(Process.myTid(), state.prevPriority);
}
}
+
public class PendingAssistExtras extends Binder implements Runnable {
public final ActivityRecord activity;
public final Bundle extras;
@@ -1072,9 +1087,9 @@
}
@Override
- public void onChange(boolean selfChange, Uri uri) {
+ public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
if (mFontScaleUri.equals(uri)) {
- updateFontScaleIfNeeded();
+ updateFontScaleIfNeeded(userId);
}
}
}
@@ -2309,6 +2324,25 @@
if (mInVrMode != vrMode) {
mInVrMode = vrMode;
mShowDialogs = shouldShowDialogs(mConfiguration, mInVrMode);
+ if (r.app != null) {
+ ProcessRecord proc = r.app;
+ if (proc.vrThreadTid > 0) {
+ if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+ try {
+ if (mInVrMode == true) {
+ Process.setThreadScheduler(proc.vrThreadTid,
+ Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
+ } else {
+ Process.setThreadScheduler(proc.vrThreadTid,
+ Process.SCHED_OTHER, 0);
+ }
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Failed to set scheduling policy, thread does"
+ + " not exist:\n" + e);
+ }
+ }
+ }
+ }
}
}
vrService.setVrMode(vrMode, requestedPackage, userId, callingPackage);
@@ -2642,6 +2676,10 @@
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
+ if (SystemProperties.getInt("sys.use_fifo_ui", 0) != 0) {
+ mUseFifoUiScheduling = true;
+ }
+
mTrackingAssociations = "1".equals(SystemProperties.get("debug.track-associations"));
mConfiguration.setToDefaults();
@@ -3747,9 +3785,6 @@
checkTime(startTime, "startProcess: returned from zygote!");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- if (app.isolated) {
- mBatteryStatsService.addIsolatedUid(app.uid, app.info.uid);
- }
mBatteryStatsService.noteProcessStart(app.processName, app.info.uid);
checkTime(startTime, "startProcess: done updating battery stats");
@@ -5432,10 +5467,10 @@
IPackageManager pm = AppGlobals.getPackageManager();
int pkgUid = -1;
synchronized(this) {
- if (getPackageManagerInternalLocked().canPackageBeWiped(
+ if (getPackageManagerInternalLocked().isPackageDataProtected(
userId, packageName)) {
throw new SecurityException(
- "Cannot clear data for a device owner or a profile owner");
+ "Cannot clear data for a protected package: " + packageName);
}
try {
@@ -6258,6 +6293,9 @@
enqueueUidChangeLocked(uidRec, -1, UidRecord.CHANGE_ACTIVE);
}
proc.uidRecord = uidRec;
+
+ // Reset render thread tid if it was already set, so new process can set it again.
+ proc.renderThreadTid = 0;
uidRec.numProcs++;
mProcessNames.put(proc.processName, proc.uid, proc);
if (proc.isolated) {
@@ -6656,8 +6694,7 @@
@Override
public void showBootMessage(final CharSequence msg, final boolean always) {
if (Binder.getCallingUid() != Process.myUid()) {
- // These days only the core system can call this, so apps can't get in
- // the way of what we show about running them.
+ throw new SecurityException();
}
mWindowManager.showBootMessage(msg, always);
}
@@ -8586,7 +8623,8 @@
if (uri == null) {
owner.removeUriPermissionsLocked(mode);
} else {
- owner.removeUriPermissionLocked(new GrantUri(userId, uri, false), mode);
+ final boolean prefix = (mode & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0;
+ owner.removeUriPermissionLocked(new GrantUri(userId, uri, prefix), mode);
}
}
}
@@ -9257,9 +9295,6 @@
// sense, so turn off auto-remove.
intent.addFlags(Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
}
- } else if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
- // Must be a new task.
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
if (!comp.equals(mLastAddedTaskComponent) || callingUid != mLastAddedTaskUid) {
mLastAddedTaskActivity = null;
@@ -11434,6 +11469,14 @@
// the uid of the isolated process is specified by the caller.
uid = isolatedUid;
}
+
+ // Register the isolated UID with this application so BatteryStats knows to
+ // attribute resource usage to the application.
+ //
+ // NOTE: This is done here before addProcessNameLocked, which will tell BatteryStats
+ // about the process state of the isolated UID *before* it is registered with the
+ // owning application.
+ mBatteryStatsService.addIsolatedUid(uid, info.uid);
}
final ProcessRecord r = new ProcessRecord(stats, info, proc, uid);
if (!mBooted && !mBooting
@@ -12034,6 +12077,9 @@
case ActivityManager.BUGREPORT_OPTION_REMOTE:
service = "bugreportremote";
break;
+ case ActivityManager.BUGREPORT_OPTION_WEAR:
+ service = "bugreportwear";
+ break;
}
if (service == null) {
throw new IllegalArgumentException("Provided bugreport type is not correct, value: "
@@ -12520,6 +12566,91 @@
}
}
+ public void setVrThread(int tid) {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
+ throw new UnsupportedOperationException("VR mode not supported on this device!");
+ }
+
+ synchronized (this) {
+ ProcessRecord proc;
+ synchronized (mPidsSelfLocked) {
+ final int pid = Binder.getCallingPid();
+ proc = mPidsSelfLocked.get(pid);
+
+ if (proc != null && mInVrMode && tid >= 0) {
+ // ensure the tid belongs to the process
+ if (!Process.isThreadInProcess(pid, tid)) {
+ throw new IllegalArgumentException("VR thread does not belong to process");
+ }
+
+ // reset existing VR thread to CFS if this thread still exists and belongs to
+ // the calling process
+ if (proc.vrThreadTid != 0
+ && Process.isThreadInProcess(pid, proc.vrThreadTid)) {
+ try {
+ Process.setThreadScheduler(proc.vrThreadTid, Process.SCHED_OTHER, 0);
+ } catch (IllegalArgumentException e) {
+ // Ignore this. Only occurs in race condition where previous VR thread
+ // was destroyed during this method call.
+ }
+ }
+
+ proc.vrThreadTid = tid;
+
+ // promote to FIFO now if the tid is non-zero
+ try {
+ if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
+ proc.vrThreadTid > 0) {
+ Process.setThreadScheduler(proc.vrThreadTid,
+ Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
+ }
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Failed to set scheduling policy, thread does"
+ + " not exist:\n" + e);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setRenderThread(int tid) {
+ synchronized (this) {
+ ProcessRecord proc;
+ synchronized (mPidsSelfLocked) {
+ int pid = Binder.getCallingPid();
+ proc = mPidsSelfLocked.get(pid);
+ if (proc != null && proc.renderThreadTid == 0 && tid > 0) {
+ // ensure the tid belongs to the process
+ if (!Process.isThreadInProcess(pid, tid)) {
+ throw new IllegalArgumentException(
+ "Render thread does not belong to process");
+ }
+ proc.renderThreadTid = tid;
+ if (DEBUG_OOM_ADJ) {
+ Slog.d("UI_FIFO", "Set RenderThread tid " + tid + " for pid " + pid);
+ }
+ // promote to FIFO now
+ if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+ if (DEBUG_OOM_ADJ) Slog.d("UI_FIFO", "Promoting " + tid + "out of band");
+ if (mUseFifoUiScheduling) {
+ Process.setThreadScheduler(proc.renderThreadTid,
+ Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
+ } else {
+ Process.setThreadPriority(proc.renderThreadTid, -10);
+ }
+ }
+ } else {
+ if (DEBUG_OOM_ADJ) {
+ Slog.d("UI_FIFO", "Didn't set thread from setRenderThread? " +
+ "PID: " + pid + ", TID: " + tid + " FIFO: " +
+ mUseFifoUiScheduling);
+ }
+ }
+ }
+ }
+ }
+
@Override
public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
@@ -12585,6 +12716,43 @@
}
}
+ @Override
+ public void setHasTopUi(boolean hasTopUi) throws RemoteException {
+ if (checkCallingPermission(permission.INTERNAL_SYSTEM_WINDOW) != PERMISSION_GRANTED) {
+ String msg = "Permission Denial: setHasTopUi() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + permission.INTERNAL_SYSTEM_WINDOW;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ final int pid = Binder.getCallingPid();
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ boolean changed = false;
+ ProcessRecord pr;
+ synchronized (mPidsSelfLocked) {
+ pr = mPidsSelfLocked.get(pid);
+ if (pr == null) {
+ Slog.w(TAG, "setHasTopUi called on unknown pid: " + pid);
+ return;
+ }
+ if (pr.hasTopUi != hasTopUi) {
+ Slog.i(TAG, "Setting hasTopUi=" + hasTopUi + " for pid=" + pid);
+ pr.hasTopUi = hasTopUi;
+ changed = true;
+ }
+ }
+ if (changed) {
+ updateOomAdjLocked(pr);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
public final void enterSafeMode() {
synchronized(this) {
// It only makes sense to do this before the system is ready
@@ -13517,7 +13685,7 @@
sb.append("Process: ").append(processName).append("\n");
int flags = process.info.flags;
IPackageManager pm = AppGlobals.getPackageManager();
- sb.append("Flags: 0x").append(Integer.toString(flags, 16)).append("\n");
+ sb.append("Flags: 0x").append(Integer.toHexString(flags)).append("\n");
for (int ip=0; ip<process.pkgList.size(); ip++) {
String pkg = process.pkgList.keyAt(ip);
sb.append("Package: ").append(pkg);
@@ -17593,6 +17761,70 @@
return INTENT_REMOTE_BUGREPORT_FINISHED.equals(intent.getAction());
}
+ private void checkBroadcastFromSystem(Intent intent, ProcessRecord callerApp,
+ String callerPackage, int callingUid, boolean isProtectedBroadcast, List receivers) {
+ final String action = intent.getAction();
+ if (isProtectedBroadcast
+ || Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
+ || Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(action)
+ || Intent.ACTION_MEDIA_BUTTON.equals(action)
+ || Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)
+ || Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(action)
+ || AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
+ || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)
+ || LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)
+ || TelephonyIntents.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action)
+ || SuggestionSpan.ACTION_SUGGESTION_PICKED.equals(action)) {
+ // Broadcast is either protected, or it's a public action that
+ // we've relaxed, so it's fine for system internals to send.
+ return;
+ }
+
+ // This broadcast may be a problem... but there are often system components that
+ // want to send an internal broadcast to themselves, which is annoying to have to
+ // explicitly list each action as a protected broadcast, so we will check for that
+ // one safe case and allow it: an explicit broadcast, only being received by something
+ // that has protected itself.
+ if (receivers != null && receivers.size() > 0
+ && (intent.getPackage() != null || intent.getComponent() != null)) {
+ boolean allProtected = true;
+ for (int i = receivers.size()-1; i >= 0; i--) {
+ Object target = receivers.get(i);
+ if (target instanceof ResolveInfo) {
+ ResolveInfo ri = (ResolveInfo)target;
+ if (ri.activityInfo.exported && ri.activityInfo.permission == null) {
+ allProtected = false;
+ break;
+ }
+ } else {
+ BroadcastFilter bf = (BroadcastFilter)target;
+ if (bf.requiredPermission == null) {
+ allProtected = false;
+ break;
+ }
+ }
+ }
+ if (allProtected) {
+ // All safe!
+ return;
+ }
+ }
+
+ // The vast majority of broadcasts sent from system internals
+ // should be protected to avoid security holes, so yell loudly
+ // to ensure we examine these cases.
+ if (callerApp != null) {
+ Log.wtf(TAG, "Sending non-protected broadcast " + action
+ + " from system " + callerApp.toShortString() + " pkg " + callerPackage,
+ new Throwable());
+ } else {
+ Log.wtf(TAG, "Sending non-protected broadcast " + action
+ + " from system uid " + UserHandle.formatUid(callingUid)
+ + " pkg " + callerPackage,
+ new Throwable());
+ }
+ }
+
final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
@@ -17679,37 +17911,9 @@
break;
}
- if (isCallerSystem) {
- if (isProtectedBroadcast
- || Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
- || Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(action)
- || Intent.ACTION_MEDIA_BUTTON.equals(action)
- || Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)
- || Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(action)
- || AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
- || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)
- || LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)
- || TelephonyIntents.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action)
- || SuggestionSpan.ACTION_SUGGESTION_PICKED.equals(action)) {
- // Broadcast is either protected, or it's a public action that
- // we've relaxed, so it's fine for system internals to send.
- } else {
- // The vast majority of broadcasts sent from system internals
- // should be protected to avoid security holes, so yell loudly
- // to ensure we examine these cases.
- if (callerApp != null) {
- Log.wtf(TAG, "Sending non-protected broadcast " + action
- + " from system " + callerApp.toShortString() + " pkg " + callerPackage,
- new Throwable());
- } else {
- Log.wtf(TAG, "Sending non-protected broadcast " + action
- + " from system uid " + UserHandle.formatUid(callingUid)
- + " pkg " + callerPackage,
- new Throwable());
- }
- }
-
- } else {
+ // First line security check before anything else: stop non-system apps from
+ // sending protected broadcasts.
+ if (!isCallerSystem) {
if (isProtectedBroadcast) {
String msg = "Permission Denial: not allowed to send broadcast "
+ action + " from pid="
@@ -18092,6 +18296,10 @@
// If we are not serializing this broadcast, then send the
// registered receivers separately so they don't wait for the
// components to be launched.
+ if (isCallerSystem) {
+ checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
+ isProtectedBroadcast, registeredReceivers);
+ }
final BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,
@@ -18179,6 +18387,11 @@
ir++;
}
+ if (isCallerSystem) {
+ checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
+ isProtectedBroadcast, receivers);
+ }
+
if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
BroadcastQueue queue = broadcastQueueForIntent(intent);
@@ -18597,23 +18810,28 @@
int userId = UserHandle.getCallingUserId();
synchronized(this) {
- final long origId = Binder.clearCallingIdentity();
- updateConfigurationLocked(values, null, false, true, userId);
+ updatePersistentConfigurationLocked(values, userId);
+ }
+ }
+
+ private void updatePersistentConfigurationLocked(Configuration values, @UserIdInt int userId) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ updateConfigurationLocked(values, null, false, true, userId, false /* deferResume */);
+ } finally {
Binder.restoreCallingIdentity(origId);
}
}
- private void updateFontScaleIfNeeded() {
- final int currentUserId;
- synchronized(this) {
- currentUserId = mUserController.getCurrentUserIdLocked();
- }
+ private void updateFontScaleIfNeeded(@UserIdInt int userId) {
final float scaleFactor = Settings.System.getFloatForUser(mContext.getContentResolver(),
- FONT_SCALE, 1.0f, currentUserId);
+ FONT_SCALE, 1.0f, userId);
if (mConfiguration.fontScale != scaleFactor) {
final Configuration configuration = mWindowManager.computeNewConfiguration();
configuration.fontScale = scaleFactor;
- updatePersistentConfiguration(configuration);
+ synchronized (this) {
+ updatePersistentConfigurationLocked(configuration, userId);
+ }
}
}
@@ -18666,11 +18884,16 @@
updateConfigurationLocked(configuration, null, false);
}
- boolean updateConfigurationLocked(Configuration values,
- ActivityRecord starting, boolean initLocale) {
+ boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+ boolean initLocale) {
+ return updateConfigurationLocked(values, starting, initLocale, false /* deferResume */);
+ }
+
+ boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+ boolean initLocale, boolean deferResume) {
// pass UserHandle.USER_NULL as userId because we don't persist configuration for any user
- return updateConfigurationLocked(values, starting, initLocale, false,
- UserHandle.USER_NULL);
+ return updateConfigurationLocked(values, starting, initLocale, false /* persistent */,
+ UserHandle.USER_NULL, deferResume);
}
// To cache the list of supported system locales
@@ -18686,8 +18909,8 @@
* @param userId is only used when persistent parameter is set to true to persist configuration
* for that particular user
*/
- private boolean updateConfigurationLocked(Configuration values,
- ActivityRecord starting, boolean initLocale, boolean persistent, int userId) {
+ private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+ boolean initLocale, boolean persistent, int userId, boolean deferResume) {
int changes = 0;
if (mWindowManager != null) {
@@ -18786,15 +19009,6 @@
null, AppOpsManager.OP_NONE, null, false, false,
MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {
- // Tell the shortcut manager that the system locale changed. It needs to know
- // it before any other apps receive ACTION_LOCALE_CHANGED, which is why
- // we "push" from here, rather than having the service listen to the broadcast.
- final ShortcutServiceInternal shortcutService =
- LocalServices.getService(ShortcutServiceInternal.class);
- if (shortcutService != null) {
- shortcutService.onSystemLocaleChangedNoLock();
- }
-
intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
if (!mProcessesReady) {
@@ -18815,7 +19029,7 @@
for (int stackId : resizedStacks) {
final Rect newBounds = mWindowManager.getBoundsForNewConfiguration(stackId);
mStackSupervisor.resizeStackLocked(
- stackId, newBounds, null, null, false, false, !DEFER_RESUME);
+ stackId, newBounds, null, null, false, false, deferResume);
}
}
}
@@ -18848,8 +19062,9 @@
/**
* Decide based on the configuration whether we should shouw the ANR,
- * crash, etc dialogs. The idea is that if there is no affordnace to
- * press the on-screen buttons, we shouldn't show the dialog.
+ * crash, etc dialogs. The idea is that if there is no affordence to
+ * press the on-screen buttons, or the user experience would be more
+ * greatly impacted than the crash itself, we shouldn't show the dialog.
*
* A thought: SystemUI might also want to get told about this, the Power
* dialog / global actions also might want different behaviors.
@@ -18858,9 +19073,10 @@
final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
&& config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
&& config.navigation == Configuration.NAVIGATION_NONAV);
- final boolean uiIsNotCarType = !((config.uiMode & Configuration.UI_MODE_TYPE_MASK)
- == Configuration.UI_MODE_TYPE_CAR);
- return inputMethodExists && uiIsNotCarType && !inVrMode;
+ int modeType = config.uiMode & Configuration.UI_MODE_TYPE_MASK;
+ final boolean uiModeSupportsDialogs = (modeType != Configuration.UI_MODE_TYPE_CAR
+ && !(modeType == Configuration.UI_MODE_TYPE_WATCH && "user".equals(Build.TYPE)));
+ return inputMethodExists && uiModeSupportsDialogs && !inVrMode;
}
@Override
@@ -19070,6 +19286,10 @@
app.systemNoUi = false;
app.curSchedGroup = ProcessList.SCHED_GROUP_TOP_APP;
app.adjType = "pers-top-activity";
+ } else if (app.hasTopUi) {
+ app.systemNoUi = false;
+ app.curSchedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+ app.adjType = "pers-top-ui";
} else if (activitiesSize > 0) {
for (int j = 0; j < activitiesSize; j++) {
final ActivityRecord r = app.activities.get(j);
@@ -19519,7 +19739,7 @@
adj = ProcessList.FOREGROUND_APP_ADJ;
if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
- schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+ schedGroup = ProcessList.SCHED_GROUP_TOP_APP_BOUND;
} else {
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
@@ -20120,6 +20340,7 @@
}
if (app.setSchedGroup != app.curSchedGroup) {
+ int oldSchedGroup = app.setSchedGroup;
app.setSchedGroup = app.curSchedGroup;
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
"Setting sched group of " + app.processName
@@ -20135,30 +20356,95 @@
processGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
break;
case ProcessList.SCHED_GROUP_TOP_APP:
+ case ProcessList.SCHED_GROUP_TOP_APP_BOUND:
processGroup = Process.THREAD_GROUP_TOP_APP;
break;
default:
processGroup = Process.THREAD_GROUP_DEFAULT;
break;
}
- if (true) {
- long oldId = Binder.clearCallingIdentity();
- try {
- Process.setProcessGroup(app.pid, processGroup);
- } catch (Exception e) {
- Slog.w(TAG, "Failed setting process group of " + app.pid
- + " to " + app.curSchedGroup);
- e.printStackTrace();
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
- } else {
- if (app.thread != null) {
- try {
- app.thread.setSchedulingGroup(processGroup);
- } catch (RemoteException e) {
+ long oldId = Binder.clearCallingIdentity();
+ try {
+ Process.setProcessGroup(app.pid, processGroup);
+ if (app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+ // do nothing if we already switched to RT
+ if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
+ // Switch VR thread for app to SCHED_FIFO
+ if (mInVrMode && app.vrThreadTid != 0) {
+ try {
+ Process.setThreadScheduler(app.vrThreadTid,
+ Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
+ } catch (IllegalArgumentException e) {
+ // thread died, ignore
+ }
+ }
+ if (mUseFifoUiScheduling) {
+ // Switch UI pipeline for app to SCHED_FIFO
+ app.savedPriority = Process.getThreadPriority(app.pid);
+ try {
+ Process.setThreadScheduler(app.pid,
+ Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
+ } catch (IllegalArgumentException e) {
+ // thread died, ignore
+ }
+ if (app.renderThreadTid != 0) {
+ try {
+ Process.setThreadScheduler(app.renderThreadTid,
+ Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
+ } catch (IllegalArgumentException e) {
+ // thread died, ignore
+ }
+ if (DEBUG_OOM_ADJ) {
+ Slog.d("UI_FIFO", "Set RenderThread (TID " +
+ app.renderThreadTid + ") to FIFO");
+ }
+ } else {
+ if (DEBUG_OOM_ADJ) {
+ Slog.d("UI_FIFO", "Not setting RenderThread TID");
+ }
+ }
+ } else {
+ // Boost priority for top app UI and render threads
+ Process.setThreadPriority(app.pid, -10);
+ if (app.renderThreadTid != 0) {
+ try {
+ Process.setThreadPriority(app.renderThreadTid, -10);
+ } catch (IllegalArgumentException e) {
+ // thread died, ignore
+ }
+ }
+ }
+ }
+ } else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
+ app.curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
+ // Reset VR thread to SCHED_OTHER
+ // Safe to do even if we're not in VR mode
+ if (app.vrThreadTid != 0) {
+ Process.setThreadScheduler(app.vrThreadTid, Process.SCHED_OTHER, 0);
+ }
+ if (mUseFifoUiScheduling) {
+ // Reset UI pipeline to SCHED_OTHER
+ Process.setThreadScheduler(app.pid, Process.SCHED_OTHER, 0);
+ Process.setThreadPriority(app.pid, app.savedPriority);
+ if (app.renderThreadTid != 0) {
+ Process.setThreadScheduler(app.renderThreadTid,
+ Process.SCHED_OTHER, 0);
+ Process.setThreadPriority(app.renderThreadTid, -4);
+ }
+ } else {
+ // Reset priority for top app UI and render threads
+ Process.setThreadPriority(app.pid, 0);
+ if (app.renderThreadTid != 0) {
+ Process.setThreadPriority(app.renderThreadTid, 0);
+ }
}
}
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed setting process group of " + app.pid
+ + " to " + app.curSchedGroup);
+ e.printStackTrace();
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
}
}
}
@@ -21321,6 +21607,11 @@
Slog.w(TAG, "No user info for user #" + targetUserId);
return false;
}
+ if (!targetUserInfo.isDemo() && UserManager.isDeviceInDemoMode(mContext)) {
+ Slog.w(TAG, "Cannot switch to non-demo user #" + targetUserId
+ + " when device is in demo mode");
+ return false;
+ }
if (!targetUserInfo.supportsSwitchTo()) {
Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported");
return false;
@@ -21356,8 +21647,9 @@
@Override
public boolean isUserRunning(int userId, int flags) {
- if (userId != UserHandle.getCallingUserId() && checkCallingPermission(
- INTERACT_ACROSS_USERS) != PackageManager.PERMISSION_GRANTED) {
+ if (!mUserController.isSameProfileGroup(userId, UserHandle.getCallingUserId())
+ && checkCallingPermission(INTERACT_ACROSS_USERS)
+ != PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: isUserRunning() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
@@ -21387,8 +21679,8 @@
}
@Override
- public void registerUserSwitchObserver(IUserSwitchObserver observer) {
- mUserController.registerUserSwitchObserver(observer);
+ public void registerUserSwitchObserver(IUserSwitchObserver observer, String name) {
+ mUserController.registerUserSwitchObserver(observer, name);
}
@Override
@@ -21662,6 +21954,48 @@
}
((PendingIntentRecord) target).setWhitelistDuration(duration);
}
+
+ @Override
+ public void updatePersistentConfigurationForUser(@NonNull Configuration values,
+ int userId) {
+ Preconditions.checkNotNull(values, "Configuration must not be null");
+ Preconditions.checkArgumentNonnegative(userId, "userId " + userId + " not supported");
+ synchronized (ActivityManagerService.this) {
+ updateConfigurationLocked(values, null, false, true, userId,
+ false /* deferResume */);
+ }
+ }
+
+ @Override
+ public int startActivitiesAsPackage(String packageName, int userId, Intent[] intents,
+ Bundle bOptions) {
+ Preconditions.checkNotNull(intents, "intents");
+ final String[] resolvedTypes = new String[intents.length];
+ for (int i = 0; i < intents.length; i++) {
+ resolvedTypes[i] = intents[i].resolveTypeIfNeeded(mContext.getContentResolver());
+ }
+
+ // UID of the package on user userId.
+ // "= 0" is needed because otherwise catch(RemoteException) would make it look like
+ // packageUid may not be initialized.
+ int packageUid = 0;
+ try {
+ packageUid = AppGlobals.getPackageManager().getPackageUid(
+ packageName, PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ }
+
+ synchronized (ActivityManagerService.this) {
+ return startActivitiesInPackage(packageUid, packageName, intents, resolvedTypes,
+ /*resultTo*/ null, bOptions, userId);
+ }
+ }
+
+ @Override
+ public int getUidProcessState(int uid) {
+ return getUidState(uid);
+ }
}
private final class SleepTokenImpl extends SleepToken {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 50b6c0c..489eb77 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -221,6 +221,13 @@
boolean pendingVoiceInteractionStart; // Waiting for activity-invoked voice session
IVoiceInteractionSession voiceSession; // Voice interaction session for this activity
+ // A hint to override the window specified rotation animation, or -1
+ // to use the window specified value. We use this so that
+ // we can select the right animation in the cases of starting
+ // windows, where the app hasn't had time to set a value
+ // on the window.
+ int mRotationAnimationHint = -1;
+
private static String startingWindowStateToString(int state) {
switch (state) {
case STARTING_WINDOW_NOT_SHOWN:
@@ -635,6 +642,7 @@
if (options != null) {
pendingOptions = options;
mLaunchTaskBehind = pendingOptions.getLaunchTaskBehind();
+ mRotationAnimationHint = pendingOptions.getRotationAnimationHint();
PendingIntent usageReport = pendingOptions.getUsageTimeReport();
if (usageReport != null) {
appTimeTracker = new AppTimeTracker(usageReport);
@@ -742,6 +750,14 @@
&& intent.getType() == null;
}
+ static boolean isMainIntent(Intent intent) {
+ return Intent.ACTION_MAIN.equals(intent.getAction())
+ && intent.hasCategory(Intent.CATEGORY_LAUNCHER)
+ && intent.getCategories().size() == 1
+ && intent.getData() == null
+ && intent.getType() == null;
+ }
+
private boolean canLaunchHomeActivity(int uid, ActivityRecord sourceRecord) {
if (uid == Process.myUid() || uid == 0) {
// System process can launch home activity.
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 5859d34..8c4c0ad 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -988,7 +988,7 @@
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep needs to pause " + mResumedActivity);
if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
"Sleep => pause with userLeaving=false");
- startPausingLocked(false, true, false, false);
+ startPausingLocked(false, true, null, false);
return true;
}
if (mPausingActivity != null) {
@@ -1066,15 +1066,16 @@
* @param userLeaving True if this should result in an onUserLeaving to the current activity.
* @param uiSleeping True if this is happening with the user interface going to sleep (the
* screen turning off).
- * @param resuming True if this is being called as part of resuming the top activity, so
- * we shouldn't try to instigate a resume here.
+ * @param resuming The activity we are currently trying to resume or null if this is not being
+ * called as part of resuming the top activity, so we shouldn't try to instigate
+ * a resume here if not null.
* @param dontWait True if the caller does not want to wait for the pause to complete. If
* set to true, we will immediately complete the pause here before returning.
* @return Returns true if an activity now is in the PAUSING state, and we are waiting for
* it to tell us when it is done.
*/
- final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,
- boolean dontWait) {
+ final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
+ ActivityRecord resuming, boolean dontWait) {
if (mPausingActivity != null) {
Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
+ " state=" + mPausingActivity.state);
@@ -1082,12 +1083,12 @@
// Avoid recursion among check for sleep and complete pause during sleeping.
// Because activity will be paused immediately after resume, just let pause
// be completed by the order of activity paused from clients.
- completePauseLocked(false);
+ completePauseLocked(false, resuming);
}
}
ActivityRecord prev = mResumedActivity;
if (prev == null) {
- if (!resuming) {
+ if (resuming == null) {
Slog.wtf(TAG, "Trying to pause when nothing is resumed");
mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
@@ -1160,7 +1161,7 @@
if (dontWait) {
// If the caller said they don't want to wait for the pause, then complete
// the pause now.
- completePauseLocked(false);
+ completePauseLocked(false, resuming);
return false;
} else {
@@ -1179,7 +1180,7 @@
// This activity failed to schedule the
// pause, so just treat it as being paused now.
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next.");
- if (!resuming) {
+ if (resuming == null) {
mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
return false;
@@ -1196,7 +1197,7 @@
if (mPausingActivity == r) {
if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSED: " + r
+ (timeout ? " (due to timeout)" : " (pause complete)"));
- completePauseLocked(true);
+ completePauseLocked(true, null);
return;
} else {
EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
@@ -1249,7 +1250,7 @@
r.stopped = true;
r.state = ActivityState.STOPPED;
- mWindowManager.notifyAppStopped(r.appToken, true);
+ mWindowManager.notifyAppStopped(r.appToken);
if (getVisibleBehindActivity() == r) {
mStackSupervisor.requestVisibleBehindLocked(r, false);
@@ -1267,7 +1268,7 @@
}
}
- private void completePauseLocked(boolean resumeNext) {
+ private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
ActivityRecord prev = mPausingActivity;
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause: " + prev);
@@ -1350,13 +1351,16 @@
prev.cpuTimeAtResume = 0; // reset it
}
- // Notify when the task stack has changed, but only if visibilities changed (not just focus)
- if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause) {
+ // Notify when the task stack has changed, but only if visibilities changed (not just
+ // focus). Also if there is an active pinned stack - we always want to notify it about
+ // task stack changes, because its positioning may depend on it.
+ if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause
+ || mService.mStackSupervisor.getStack(PINNED_STACK_ID) != null) {
mService.notifyTaskStackChangedLocked();
mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
}
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mStackSupervisor.ensureActivitiesVisibleLocked(resuming, 0, !PRESERVE_WINDOWS);
}
private void addToStopping(ActivityRecord r, boolean immediate) {
@@ -2048,6 +2052,14 @@
// We don't want to clear starting window for activities that aren't behind fullscreen
// activities as we need to display their starting window until they are done initializing.
boolean behindFullscreenActivity = false;
+
+ if (getStackVisibilityLocked(null) == STACK_INVISIBLE) {
+ // The stack is not visible, so no activity in it should be displaying a starting
+ // window. Mark all activities below top and behind fullscreen.
+ aboveTop = false;
+ behindFullscreenActivity = true;
+ }
+
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
@@ -2245,11 +2257,11 @@
// We need to start pausing the current activity so the top one can be resumed...
final boolean dontWaitForPause = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0;
- boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, true, dontWaitForPause);
+ boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, dontWaitForPause);
if (mResumedActivity != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: Pausing " + mResumedActivity);
- pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause);
+ pausing |= startPausingLocked(userLeaving, false, next, dontWaitForPause);
}
if (pausing) {
if (DEBUG_SWITCH || DEBUG_STATES) Slog.v(TAG_STATES,
@@ -2473,13 +2485,25 @@
}
}
+ boolean allowSavedSurface = true;
if (next.newIntents != null) {
+ // Restrict saved surface to launcher start, or there is no intent at all
+ // (eg. task being brought to front). If the intent is something else,
+ // likely the app is going to show some specific page or view, instead of
+ // what's left last time.
+ for (int i = next.newIntents.size() - 1; i >= 0; i--) {
+ final Intent intent = next.newIntents.get(i);
+ if (intent != null && !ActivityRecord.isMainIntent(intent)) {
+ allowSavedSurface = false;
+ break;
+ }
+ }
next.app.thread.scheduleNewIntent(next.newIntents, next.appToken);
}
// Well the app will no longer be stopped.
// Clear app token stopped state in window manager if needed.
- mWindowManager.notifyAppStopped(next.appToken, false);
+ mWindowManager.notifyAppResumed(next.appToken, next.stopped, allowSavedSurface);
EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId,
System.identityHashCode(next), next.task.taskId, next.shortComponentName);
@@ -2587,11 +2611,14 @@
}
private void insertTaskAtTop(TaskRecord task, ActivityRecord newActivity) {
+ boolean isLastTaskOverHome = false;
// If the moving task is over home stack, transfer its return type to next task
if (task.isOverHomeStack()) {
final TaskRecord nextTask = getNextTask(task);
if (nextTask != null) {
nextTask.setTaskToReturnTo(task.getTaskToReturnTo());
+ } else {
+ isLastTaskOverHome = true;
}
}
@@ -2601,7 +2628,10 @@
ActivityStack lastStack = mStackSupervisor.getLastStack();
final boolean fromHome = lastStack.isHomeStack();
if (!isHomeStack() && (fromHome || topTask() != task)) {
- int returnToType = APPLICATION_ACTIVITY_TYPE;
+ // If it's a last task over home - we default to keep its return to type not to
+ // make underlying task focused when this one will be finished.
+ int returnToType = isLastTaskOverHome
+ ? task.getTaskToReturnTo() : APPLICATION_ACTIVITY_TYPE;
if (fromHome && StackId.allowTopTaskToReturnHome(mStackId)) {
returnToType = lastStack.topTask() == null
? HOME_ACTIVITY_TYPE : lastStack.topTask().taskType;
@@ -3480,7 +3510,7 @@
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish needs to pause: " + r);
if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
"finish() => pause with userLeaving=false");
- startPausingLocked(false, false, false, false);
+ startPausingLocked(false, false, null, false);
}
if (endTask) {
@@ -3770,6 +3800,10 @@
if (getVisibleBehindActivity() == r) {
mStackSupervisor.requestVisibleBehindLocked(r, false);
}
+
+ // Clean-up activities are no longer relaunching (e.g. app process died). Notify window
+ // manager so it can update its bookkeeping.
+ mWindowManager.notifyAppRelaunchesCleared(r.appToken);
}
private void removeTimeoutsForActivityLocked(ActivityRecord r) {
@@ -5205,7 +5239,7 @@
(r.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, r.info.configChanges,
task.voiceSession != null, r.mLaunchTaskBehind, bounds, task.mOverrideConfig,
task.mResizeMode, r.isAlwaysFocusable(), task.isHomeTask(),
- r.appInfo.targetSdkVersion);
+ r.appInfo.targetSdkVersion, r.mRotationAnimationHint);
r.taskConfigOverride = task.mOverrideConfig;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 06e5ea2..1cb83d2 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -781,20 +781,25 @@
}
}
+ static int nextTaskIdForUser(int taskId, int userId) {
+ int nextTaskId = taskId + 1;
+ if (nextTaskId == (userId + 1) * MAX_TASK_IDS_PER_USER) {
+ // Wrap around as there will be smaller task ids that are available now.
+ nextTaskId -= MAX_TASK_IDS_PER_USER;
+ }
+ return nextTaskId;
+ }
+
int getNextTaskIdForUserLocked(int userId) {
final int currentTaskId = mCurTaskIdForUser.get(userId, userId * MAX_TASK_IDS_PER_USER);
// for a userId u, a taskId can only be in the range
// [u*MAX_TASK_IDS_PER_USER, (u+1)*MAX_TASK_IDS_PER_USER-1], so if MAX_TASK_IDS_PER_USER
// was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on.
- int candidateTaskId = currentTaskId;
+ int candidateTaskId = nextTaskIdForUser(currentTaskId, userId);
while (mRecentTasks.taskIdTakenForUserLocked(candidateTaskId, userId)
|| anyTaskForIdLocked(candidateTaskId, !RESTORE_FROM_RECENTS,
INVALID_STACK_ID) != null) {
- candidateTaskId++;
- if (candidateTaskId == (userId + 1) * MAX_TASK_IDS_PER_USER) {
- // Wrap around as there will be smaller task ids that are available now.
- candidateTaskId -= MAX_TASK_IDS_PER_USER;
- }
+ candidateTaskId = nextTaskIdForUser(candidateTaskId, userId);
if (candidateTaskId == currentTaskId) {
// Something wrong!
// All MAX_TASK_IDS_PER_USER task ids are taken up by running tasks for this user
@@ -871,6 +876,8 @@
}
}
}
+ // Send launch end powerhint when idle
+ mService.mActivityStarter.sendPowerHintForLaunchEndIfNeeded();
return true;
}
@@ -916,9 +923,12 @@
/**
* Pause all activities in either all of the stacks or just the back stacks.
* @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
+ * @param resuming The resuming activity.
+ * @param dontWait The resuming activity isn't going to wait for all activities to be paused
+ * before resuming.
* @return true if any activity was paused as a result of this call.
*/
- boolean pauseBackStacks(boolean userLeaving, boolean resuming, boolean dontWait) {
+ boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
boolean someActivityPaused = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
@@ -957,7 +967,7 @@
}
void pauseChildStacks(ActivityRecord parent, boolean userLeaving, boolean uiSleeping,
- boolean resuming, boolean dontWait) {
+ ActivityRecord resuming, boolean dontWait) {
// TODO: Put all stacks in supervisor and iterate through them instead.
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
@@ -1187,7 +1197,10 @@
Configuration config = mWindowManager.updateOrientationFromAppTokens(
mService.mConfiguration,
r.mayFreezeScreenLocked(app) ? r.appToken : null);
- mService.updateConfigurationLocked(config, r, false);
+ // Deferring resume here because we're going to launch new activity shortly.
+ // We don't want to perform a redundant launch of the same record while ensuring
+ // configurations and trying to resume top activity of focused stack.
+ mService.updateConfigurationLocked(config, r, false, true /* deferResume */);
}
r.app = app;
@@ -2002,7 +2015,7 @@
boolean preserveWindows, boolean allowResizeInDockedMode, boolean deferResume) {
if (stackId == DOCKED_STACK_ID) {
resizeDockedStackLocked(bounds, tempTaskBounds, tempTaskInsetBounds, null, null,
- preserveWindows);
+ preserveWindows, deferResume);
return;
}
final ActivityStack stack = getStack(stackId);
@@ -2152,8 +2165,16 @@
}
void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
- Rect tempDockedTaskInsetBounds,
- Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds, boolean preserveWindows) {
+ Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
+ boolean preserveWindows) {
+ resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds, tempDockedTaskInsetBounds,
+ tempOtherTaskBounds, tempOtherTaskInsetBounds, preserveWindows,
+ false /* deferResume */);
+ }
+
+ void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
+ Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
+ boolean preserveWindows, boolean deferResume) {
if (!mAllowDockedStackResize) {
// Docked stack resize currently disabled.
@@ -2196,11 +2217,13 @@
if (StackId.isResizeableByDockedStack(i) && getStack(i) != null) {
resizeStackLocked(i, tempRect, tempOtherTaskBounds,
tempOtherTaskInsetBounds, preserveWindows,
- true /* allowResizeInDockedMode */, !DEFER_RESUME);
+ true /* allowResizeInDockedMode */, deferResume);
}
}
}
- stack.ensureVisibleActivitiesConfigurationLocked(r, preserveWindows);
+ if (!deferResume) {
+ stack.ensureVisibleActivitiesConfigurationLocked(r, preserveWindows);
+ }
} finally {
mAllowDockedStackResize = true;
mWindowManager.continueSurfaceLayout();
@@ -2764,6 +2787,9 @@
}
}
+ // Send launch end powerhint before going sleep
+ mService.mActivityStarter.sendPowerHintForLaunchEndIfNeeded();
+
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
@@ -3056,7 +3082,7 @@
final boolean nowVisible = allResumedActivitiesVisible();
for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord s = mStoppingActivities.get(activityNdx);
- final boolean waitingVisible = mWaitingVisibleActivities.contains(s);
+ boolean waitingVisible = mWaitingVisibleActivities.contains(s);
if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible
+ " waitingVisible=" + waitingVisible + " finishing=" + s.finishing);
if (waitingVisible && nowVisible) {
@@ -3069,6 +3095,7 @@
// hidden by the activities in front of it.
if (DEBUG_STATES) Slog.v(TAG, "Before stopping, can hide: " + s);
mWindowManager.setAppVisibility(s.appToken, false);
+ waitingVisible = false;
}
}
if ((!waitingVisible || mService.isSleepingOrShuttingDownLocked()) && remove) {
@@ -4176,7 +4203,7 @@
mContainerState = CONTAINER_STATE_NO_SURFACE;
((VirtualActivityDisplay) mActivityDisplay).setSurface(null);
if (mStack.mPausingActivity == null && mStack.mResumedActivity != null) {
- mStack.startPausingLocked(false, true, false, false);
+ mStack.startPausingLocked(false, true, null, false);
}
}
@@ -4432,6 +4459,7 @@
// Work Challenge is present) let startActivityInPackage handle the intercepting.
if (!mService.mUserController.shouldConfirmCredentials(task.userId)
&& task.getRootActivity() != null) {
+ mService.mActivityStarter.sendPowerHintForLaunchStartIfNeeded(true /* forceSend */);
mActivityMetricsLogger.notifyActivityLaunching();
mService.moveTaskToFrontLocked(task.taskId, 0, bOptions);
mActivityMetricsLogger.notifyActivityLaunched(ActivityManager.START_TASK_TO_FRONT,
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 7b3f65a..5252446 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -102,6 +102,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.PowerManagerInternal;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -173,6 +174,7 @@
private boolean mNoAnimation;
private boolean mKeepCurTransition;
private boolean mAvoidMoveToFront;
+ private boolean mPowerHintSent;
private IVoiceInteractionSession mVoiceSession;
private IVoiceInteractor mVoiceInteractor;
@@ -893,13 +895,15 @@
throw new IllegalArgumentException("intents are length different than resolvedTypes");
}
+ final int realCallingPid = Binder.getCallingPid();
+ final int realCallingUid = Binder.getCallingUid();
int callingPid;
if (callingUid >= 0) {
callingPid = -1;
} else if (caller == null) {
- callingPid = Binder.getCallingPid();
- callingUid = Binder.getCallingUid();
+ callingPid = realCallingPid;
+ callingUid = realCallingUid;
} else {
callingPid = callingUid = -1;
}
@@ -940,7 +944,8 @@
i == intents.length - 1 ? bOptions : null);
int res = startActivityLocked(caller, intent, null /*ephemeralIntent*/,
resolvedTypes[i], aInfo, null /*rInfo*/, null, null, resultTo, null, -1,
- callingPid, callingUid, callingPackage, callingPid, callingUid, 0,
+ callingPid, callingUid, callingPackage,
+ realCallingPid, realCallingUid, 0,
options, false, componentSpecified, outActivity, null, null);
if (res < 0) {
return res;
@@ -956,6 +961,28 @@
return START_SUCCESS;
}
+ void sendPowerHintForLaunchStartIfNeeded(boolean forceSend) {
+ // Trigger launch power hint if activity being launched is not in the current task
+ final ActivityStack focusStack = mSupervisor.getFocusedStack();
+ final ActivityRecord curTop = (focusStack == null)
+ ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
+ if ((forceSend || (!mPowerHintSent && curTop != null &&
+ curTop.task != null && mStartActivity != null &&
+ curTop.task != mStartActivity.task )) &&
+ mService.mLocalPowerManager != null) {
+ mService.mLocalPowerManager.powerHint(PowerManagerInternal.POWER_HINT_LAUNCH, 1);
+ mPowerHintSent = true;
+ }
+ }
+
+ void sendPowerHintForLaunchEndIfNeeded() {
+ // Trigger launch power hint if activity is launched
+ if (mPowerHintSent && mService.mLocalPowerManager != null) {
+ mService.mLocalPowerManager.powerHint(PowerManagerInternal.POWER_HINT_LAUNCH, 0);
+ mPowerHintSent = false;
+ }
+ }
+
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
@@ -1017,6 +1044,8 @@
}
}
+ sendPowerHintForLaunchStartIfNeeded(false /* forceSend */);
+
mReusedActivity = setTargetStackAndMoveToFrontIfNeeded(mReusedActivity);
if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
@@ -1095,7 +1124,11 @@
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
if (!mMovedOtherTask) {
- updateTaskReturnToType(mStartActivity.task, mLaunchFlags, topStack);
+ // If stack id is specified in activity options, usually it means that activity is
+ // launched not from currently focused stack (e.g. from SysUI or from shell) - in
+ // that case we check the target stack.
+ updateTaskReturnToType(mStartActivity.task, mLaunchFlags,
+ preferredLaunchStackId != INVALID_STACK_ID ? mTargetStack : topStack);
}
} else if (mSourceRecord != null) {
if (mSupervisor.isLockTaskModeViolation(mSourceRecord.task)) {
@@ -1138,6 +1171,9 @@
ActivityStack.logStartActivity(
EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.task);
mTargetStack.mLastPausedActivity = null;
+
+ sendPowerHintForLaunchStartIfNeeded(false /* forceSend */);
+
mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);
if (mDoResume) {
if (!mLaunchTaskBehind) {
diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index e37feb2..646f6ce 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -16,29 +16,22 @@
package com.android.server.am;
-import android.app.ActivityManagerInternal;
-import android.app.ActivityOptions;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.IPackageDataObserver;
-import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.text.BidiFormatter;
-import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.TextView;
-import java.util.List;
-
import static com.android.server.am.ActivityManagerService.IS_USER_BUILD;
final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListener {
@@ -47,7 +40,7 @@
private final AppErrorResult mResult;
private final ProcessRecord mProc;
private final boolean mRepeating;
- private final boolean mForeground;
+ private final boolean mIsRestartable;
private CharSequence mName;
@@ -74,7 +67,7 @@
mProc = data.proc;
mResult = data.result;
mRepeating = data.repeating;
- mForeground = data.task != null;
+ mIsRestartable = data.task != null || data.isRestartableForService;
BidiFormatter bidi = BidiFormatter.getInstance();
if ((mProc.pkgList.size() == 1) &&
@@ -118,7 +111,7 @@
LayoutInflater.from(context).inflate(
com.android.internal.R.layout.app_error_dialog, frame, true);
- boolean hasRestart = !mRepeating && mForeground;
+ boolean hasRestart = !mRepeating && mIsRestartable;
final boolean hasReceiver = mProc.errorReportReceiver != null;
final TextView restart = (TextView) findViewById(com.android.internal.R.id.aerr_restart);
@@ -214,5 +207,6 @@
TaskRecord task;
boolean repeating;
ProcessRecord proc;
+ boolean isRestartableForService;
}
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index cb37999..7eff773 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -58,6 +58,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Set;
import java.util.concurrent.Semaphore;
import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
@@ -386,8 +387,8 @@
} catch (IllegalArgumentException e) {
// Hmm, that didn't work, app might have crashed before creating a
// recents entry. Let's see if we have a safe-to-restart intent.
- if (task.intent.getCategories().contains(
- Intent.CATEGORY_LAUNCHER)) {
+ final Set<String> cats = task.intent.getCategories();
+ if (cats != null && cats.contains(Intent.CATEGORY_LAUNCHER)) {
mService.startActivityInPackage(task.mCallingUid,
task.mCallingPackage, task.intent,
null, null, null, 0, 0,
@@ -626,12 +627,21 @@
}
}
+ boolean procIsBoundForeground =
+ (app.curProcState == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
// Bump up the crash count of any services currently running in the proc.
for (int i=app.services.size()-1; i>=0; i--) {
// Any services running in the application need to be placed
// back in the pending list.
ServiceRecord sr = app.services.valueAt(i);
sr.crashCount++;
+
+ // Allow restarting for started or bound foreground services that are crashing the
+ // first time. This includes wallpapers.
+ if ((data != null) && (sr.crashCount <= 1)
+ && (sr.isForeground || procIsBoundForeground)) {
+ data.isRestartableForService = true;
+ }
}
// If the crashing process is what we consider to be the "home process" and it has been
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index def6828..ff13125 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -484,6 +484,20 @@
}
}
+ public void noteLongPartialWakelockStart(String name, String historyName, int uid) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteLongPartialWakelockStart(name, historyName, uid);
+ }
+ }
+
+ public void noteLongPartialWakelockFinish(String name, String historyName, int uid) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteLongPartialWakelockFinish(name, historyName, uid);
+ }
+ }
+
public void noteStartSensor(int uid, int sensor) {
enforceCallingPermission();
synchronized (mStats) {
@@ -710,7 +724,7 @@
}
@Override
- public void noteWifiRadioPowerState(int powerState, long tsNanos) {
+ public void noteWifiRadioPowerState(int powerState, long tsNanos, int uid) {
enforceCallingPermission();
// There was a change in WiFi power state.
@@ -723,7 +737,7 @@
mHandler.scheduleSync("wifi-data: " + type,
BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI);
}
- mStats.noteWifiRadioPowerState(powerState, tsNanos);
+ mStats.noteWifiRadioPowerState(powerState, tsNanos, uid);
}
}
diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java
index 1f3ccf5..f4f6b66 100644
--- a/services/core/java/com/android/server/am/PreBootBroadcaster.java
+++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java
@@ -19,17 +19,24 @@
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import android.app.AppOpsManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.ComponentName;
+import android.content.Context;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
import android.os.Process;
import android.os.UserHandle;
import android.util.Slog;
import com.android.internal.R;
import com.android.internal.util.ProgressReporter;
+import com.android.server.UiThread;
import java.util.List;
@@ -44,6 +51,7 @@
private final ActivityManagerService mService;
private final int mUserId;
private final ProgressReporter mProgress;
+ private final boolean mQuiet;
private final Intent mIntent;
private final List<ResolveInfo> mTargets;
@@ -51,10 +59,11 @@
private int mIndex = 0;
public PreBootBroadcaster(ActivityManagerService service, int userId,
- ProgressReporter progress) {
+ ProgressReporter progress, boolean quiet) {
mService = service;
mUserId = userId;
mProgress = progress;
+ mQuiet = quiet;
mIntent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
mIntent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE | Intent.FLAG_DEBUG_TRIAGED_MISSING);
@@ -65,16 +74,22 @@
public void sendNext() {
if (mIndex >= mTargets.size()) {
+ mHandler.obtainMessage(MSG_HIDE).sendToTarget();
onFinished();
return;
}
if (!mService.isUserRunning(mUserId, 0)) {
Slog.i(TAG, "User " + mUserId + " is no longer running; skipping remaining receivers");
+ mHandler.obtainMessage(MSG_HIDE).sendToTarget();
onFinished();
return;
}
+ if (!mQuiet) {
+ mHandler.obtainMessage(MSG_SHOW, mTargets.size(), mIndex).sendToTarget();
+ }
+
final ResolveInfo ri = mTargets.get(mIndex++);
final ComponentName componentName = ri.activityInfo.getComponentName();
@@ -100,5 +115,58 @@
sendNext();
}
+ private static final int MSG_SHOW = 1;
+ private static final int MSG_HIDE = 2;
+
+ private Handler mHandler = new Handler(UiThread.get().getLooper(), null, true) {
+ @Override
+ public void handleMessage(Message msg) {
+ final Context context = mService.mContext;
+ final NotificationManager notifManager = context
+ .getSystemService(NotificationManager.class);
+ final int max = msg.arg1;
+ final int index = msg.arg2;
+
+ switch (msg.what) {
+ case MSG_SHOW:
+ final CharSequence title = context
+ .getText(R.string.android_upgrading_notification_title);
+
+ final Intent intent = new Intent();
+ intent.setClassName("com.android.settings",
+ "com.android.settings.HelpTrampoline");
+ intent.putExtra(Intent.EXTRA_TEXT, "help_url_upgrading");
+
+ final PendingIntent contentIntent;
+ if (context.getPackageManager().resolveActivity(intent, 0) != null) {
+ contentIntent = PendingIntent.getActivity(context, 0, intent, 0);
+ } else {
+ contentIntent = null;
+ }
+
+ final Notification notif = new Notification.Builder(mService.mContext)
+ .setSmallIcon(R.drawable.stat_sys_adb)
+ .setWhen(0)
+ .setOngoing(true)
+ .setTicker(title)
+ .setDefaults(0)
+ .setPriority(Notification.PRIORITY_MAX)
+ .setColor(context.getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setContentTitle(title)
+ .setContentIntent(contentIntent)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setProgress(max, index, false)
+ .build();
+ notifManager.notifyAsUser(TAG, 0, notif, UserHandle.of(mUserId));
+ break;
+
+ case MSG_HIDE:
+ notifManager.cancelAsUser(TAG, 0, UserHandle.of(mUserId));
+ break;
+ }
+ }
+ };
+
public abstract void onFinished();
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index f073e5c..475b155 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -130,6 +130,9 @@
static final int SCHED_GROUP_DEFAULT = 1;
// Activity manager's version of Process.THREAD_GROUP_TOP_APP
static final int SCHED_GROUP_TOP_APP = 2;
+ // Activity manager's version of Process.THREAD_GROUP_TOP_APP
+ // Disambiguate between actual top app and processes bound to the top app
+ static final int SCHED_GROUP_TOP_APP_BOUND = 3;
// The minimum number of cached apps we want to be able to keep around,
// without empty apps being able to push them out of memory.
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 8911a3e..3fffefb 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -97,11 +97,14 @@
int verifiedAdj; // The last adjustment that was verified as actually being set
int curSchedGroup; // Currently desired scheduling class
int setSchedGroup; // Last set to background scheduling class
+ int vrThreadTid; // Thread currently set for VR scheduling
int trimMemoryLevel; // Last selected memory trimming level
int curProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
int repProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
int setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process tracker
int pssProcState = PROCESS_STATE_NONEXISTENT; // Currently requesting pss for
+ int savedPriority; // Previous priority value if we're switching to non-SCHED_OTHER
+ int renderThreadTid; // TID for RenderThread
boolean serviceb; // Process currently is on the service B list
boolean serviceHighRam; // We are forcing to service B list due to its RAM use
boolean setIsForeground; // Running foreground UI when last set?
@@ -113,6 +116,8 @@
boolean repForegroundActivities; // Last reported foreground activities.
boolean systemNoUi; // This is a system process, but not currently showing UI.
boolean hasShownUi; // Has UI been shown in this process since it was started?
+ boolean hasTopUi; // Is this process currently showing "top-level" UI that is not an
+ // activity?
boolean pendingUiClean; // Want to clean up resources from showing UI?
boolean hasAboveClient; // Bound using BIND_ABOVE_CLIENT, so want to be lower
boolean treatLikeActivity; // Bound using BIND_TREAT_LIKE_ACTIVITY
@@ -293,6 +298,7 @@
pw.print(" setSchedGroup="); pw.print(setSchedGroup);
pw.print(" systemNoUi="); pw.print(systemNoUi);
pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel);
+ pw.print(prefix); pw.print("vrThreadTid="); pw.print(vrThreadTid);
pw.print(prefix); pw.print("curProcState="); pw.print(curProcState);
pw.print(" repProcState="); pw.print(repProcState);
pw.print(" pssProcState="); pw.print(pssProcState);
@@ -430,6 +436,9 @@
pw.print(prefix); pw.print(" - "); pw.println(receivers.valueAt(i));
}
}
+ if (hasTopUi) {
+ pw.print(prefix); pw.print("hasTopUi="); pw.print(hasTopUi);
+ }
}
ProcessRecord(BatteryStatsImpl _batteryStats, ApplicationInfo _info,
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 5c05ab6..beb863b 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -653,12 +653,17 @@
&& task.realActivity.equals(tr.realActivity);
// If the document is open in another app or is not the same
// document, we don't need to trim it.
- if (!sameActivity || !sameIntentFilter || multiTasksAllowed) {
+ if (!sameActivity) {
continue;
// Otherwise only trim if we are over our max recents for this task
- } else if (maxRecents > 0 && !doTrim) {
+ } else if (maxRecents > 0) {
--maxRecents;
- continue;
+ if (!doTrim || !sameIntentFilter || multiTasksAllowed) {
+ // We don't want to trim if we are not over the max allowed entries and
+ // the caller doesn't want us to trim, the tasks are not of the same
+ // intent filter, or multiple entries fot the task is allowed.
+ continue;
+ }
}
// Hit the maximum number of documents for this task. Fall through
// and remove this document from recents.
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 2bfc402..71c7fd3 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -543,27 +543,25 @@
}
public void cancelNotification() {
- if (foregroundId != 0) {
- // Do asynchronous communication with notification manager to
- // avoid deadlocks.
- final String localPackageName = packageName;
- final int localForegroundId = foregroundId;
- ams.mHandler.post(new Runnable() {
- public void run() {
- INotificationManager inm = NotificationManager.getService();
- if (inm == null) {
- return;
- }
- try {
- inm.cancelNotificationWithTag(localPackageName, null,
- localForegroundId, userId);
- } catch (RuntimeException e) {
- Slog.w(TAG, "Error canceling notification for service", e);
- } catch (RemoteException e) {
- }
+ // Do asynchronous communication with notification manager to
+ // avoid deadlocks.
+ final String localPackageName = packageName;
+ final int localForegroundId = foregroundId;
+ ams.mHandler.post(new Runnable() {
+ public void run() {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
}
- });
- }
+ try {
+ inm.cancelNotificationWithTag(localPackageName, null,
+ localForegroundId, userId);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Error canceling notification for service", e);
+ } catch (RemoteException e) {
+ }
+ }
+ });
}
public void stripForegroundServiceFlagFromNotification() {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index b685dd3..4bc148b 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -76,6 +76,7 @@
import android.os.UserManagerInternal;
import android.os.storage.IMountService;
import android.os.storage.StorageManager;
+import android.util.ArraySet;
import android.util.IntArray;
import android.util.Pair;
import android.util.Slog;
@@ -86,6 +87,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerService;
@@ -97,6 +99,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Helper class for {@link ActivityManagerService} responsible for multi-user functionality.
@@ -151,9 +154,9 @@
= new RemoteCallbackList<>();
/**
- * Currently active user switch.
+ * Currently active user switch callbacks.
*/
- Object mCurUserSwitchCallback;
+ private volatile ArraySet<String> mCurWaitingUserSwitchCallbacks;
private volatile UserManagerService mUserManager;
@@ -224,6 +227,7 @@
private void finishUserBoot(UserState uss, IIntentReceiver resultTo) {
final int userId = uss.mHandle.getIdentifier();
+
Slog.d(TAG, "Finishing user boot " + userId);
synchronized (mService) {
// Bail if we ended up with a stale user
@@ -275,6 +279,7 @@
*/
private void finishUserUnlocking(final UserState uss) {
final int userId = uss.mHandle.getIdentifier();
+ boolean proceedWithUnlock = false;
synchronized (mService) {
// Bail if we ended up with a stale user
if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
@@ -284,20 +289,24 @@
if (uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) {
getUserManagerInternal().setUserState(userId, uss.state);
- uss.mUnlockProgress.start();
-
- // Prepare app storage before we go any further
- uss.mUnlockProgress.setProgress(5,
- mService.mContext.getString(R.string.android_start_title));
- mUserManager.onBeforeUnlockUser(userId);
- uss.mUnlockProgress.setProgress(20);
-
- // Dispatch unlocked to system services; when fully dispatched,
- // that calls through to the next "unlocked" phase
- mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
- .sendToTarget();
+ proceedWithUnlock = true;
}
}
+
+ if (proceedWithUnlock) {
+ uss.mUnlockProgress.start();
+
+ // Prepare app storage before we go any further
+ uss.mUnlockProgress.setProgress(5,
+ mService.mContext.getString(R.string.android_start_title));
+ mUserManager.onBeforeUnlockUser(userId);
+ uss.mUnlockProgress.setProgress(20);
+
+ // Dispatch unlocked to system services; when fully dispatched,
+ // that calls through to the next "unlocked" phase
+ mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
+ .sendToTarget();
+ }
}
/**
@@ -347,7 +356,17 @@
// PRE_BOOT receivers are finished to avoid ANR'ing apps
final UserInfo info = getUserInfo(userId);
if (!Objects.equals(info.lastLoggedInFingerprint, Build.FINGERPRINT)) {
- new PreBootBroadcaster(mService, userId, null) {
+ // Suppress double notifications for managed profiles that
+ // were unlocked automatically as part of their parent user
+ // being unlocked.
+ final boolean quiet;
+ if (info.isManagedProfile()) {
+ quiet = !uss.tokenProvided
+ || !mLockPatternUtils.isSeparateProfileChallengeEnabled(userId);
+ } else {
+ quiet = false;
+ }
+ new PreBootBroadcaster(mService, userId, null, quiet) {
@Override
public void onFinished() {
finishUserUnlockedCompleted(uss);
@@ -948,6 +967,7 @@
boolean unlockUserCleared(final int userId, byte[] token, byte[] secret,
IProgressListener listener) {
+ UserState uss;
synchronized (mService) {
// TODO Move this block outside of synchronized if it causes lock contention
if (!StorageManager.isUserKeyUnlocked(userId)) {
@@ -962,15 +982,20 @@
}
// Bail if user isn't actually running, otherwise register the given
// listener to watch for unlock progress
- final UserState uss = mStartedUsers.get(userId);
+ uss = mStartedUsers.get(userId);
if (uss == null) {
notifyFinished(userId, listener);
return false;
} else {
uss.mUnlockProgress.addListener(listener);
+ uss.tokenProvided = (token != null);
}
+ }
- finishUserUnlocking(uss);
+ finishUserUnlocking(uss);
+
+ final ArraySet<Integer> childProfilesToUnlock = new ArraySet<>();
+ synchronized (mService) {
// We just unlocked a user, so let's now attempt to unlock any
// managed profiles under that user.
@@ -980,11 +1005,16 @@
if (parent != null && parent.id == userId && testUserId != userId) {
Slog.d(TAG, "User " + testUserId + " (parent " + parent.id
+ "): attempting unlock because parent was just unlocked");
- maybeUnlockUser(testUserId);
+ childProfilesToUnlock.add(testUserId);
}
}
}
+ final int size = childProfilesToUnlock.size();
+ for (int i = 0; i < size; i++) {
+ maybeUnlockUser(childProfilesToUnlock.valueAt(i));
+ }
+
return true;
}
@@ -1039,7 +1069,8 @@
void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) {
synchronized (mService) {
- Slog.wtf(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId);
+ Slog.wtf(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId
+ + ". Observers that didn't send results: " + mCurWaitingUserSwitchCallbacks);
sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
}
}
@@ -1048,28 +1079,37 @@
Slog.d(TAG, "Dispatch onUserSwitching oldUser #" + oldUserId + " newUser #" + newUserId);
final int observerCount = mUserSwitchObservers.beginBroadcast();
if (observerCount > 0) {
- final IRemoteCallback callback = new IRemoteCallback.Stub() {
- int mCount = 0;
- @Override
- public void sendResult(Bundle data) throws RemoteException {
- synchronized (mService) {
- if (mCurUserSwitchCallback == this) {
- mCount++;
- if (mCount == observerCount) {
- sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
- }
- }
- }
- }
- };
+ final ArraySet<String> curWaitingUserSwitchCallbacks = new ArraySet<>();
synchronized (mService) {
uss.switching = true;
- mCurUserSwitchCallback = callback;
+ mCurWaitingUserSwitchCallbacks = curWaitingUserSwitchCallbacks;
}
+ final AtomicInteger waitingCallbacksCount = new AtomicInteger(observerCount);
for (int i = 0; i < observerCount; i++) {
try {
- mUserSwitchObservers.getBroadcastItem(i).onUserSwitching(
- newUserId, callback);
+ // Prepend with unique prefix to guarantee that keys are unique
+ final String name = "#" + i + " " + mUserSwitchObservers.getBroadcastCookie(i);
+ synchronized (mService) {
+ curWaitingUserSwitchCallbacks.add(name);
+ }
+ final IRemoteCallback callback = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ synchronized (mService) {
+ // Early return if this session is no longer valid
+ if (curWaitingUserSwitchCallbacks
+ != mCurWaitingUserSwitchCallbacks) {
+ return;
+ }
+ curWaitingUserSwitchCallbacks.remove(name);
+ // Continue switching if all callbacks have been notified
+ if (waitingCallbacksCount.decrementAndGet() == 0) {
+ sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
+ }
+ }
+ }
+ };
+ mUserSwitchObservers.getBroadcastItem(i).onUserSwitching(newUserId, callback);
} catch (RemoteException e) {
}
}
@@ -1082,7 +1122,7 @@
}
void sendContinueUserSwitchLocked(UserState uss, int oldUserId, int newUserId) {
- mCurUserSwitchCallback = null;
+ mCurWaitingUserSwitchCallbacks = null;
mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
mHandler.sendMessage(mHandler.obtainMessage(ActivityManagerService.CONTINUE_USER_SWITCH_MSG,
oldUserId, newUserId, uss));
@@ -1247,7 +1287,8 @@
? getCurrentUserIdLocked(): userId;
}
- void registerUserSwitchObserver(IUserSwitchObserver observer) {
+ void registerUserSwitchObserver(IUserSwitchObserver observer, String name) {
+ Preconditions.checkNotNull(name, "Observer name cannot be null");
if (mService.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
!= PackageManager.PERMISSION_GRANTED) {
final String msg = "Permission Denial: registerUserSwitchObserver() from pid="
@@ -1257,8 +1298,7 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
-
- mUserSwitchObservers.register(observer);
+ mUserSwitchObservers.register(observer, name);
}
void unregisterUserSwitchObserver(IUserSwitchObserver observer) {
@@ -1453,6 +1493,9 @@
}
boolean isSameProfileGroup(int callingUserId, int targetUserId) {
+ if (callingUserId == targetUserId) {
+ return true;
+ }
synchronized (mUserProfileGroupIdsSelfLocked) {
int callingProfile = mUserProfileGroupIdsSelfLocked.get(callingUserId,
UserInfo.NO_PROFILE_GROUP_ID);
diff --git a/services/core/java/com/android/server/am/UserState.java b/services/core/java/com/android/server/am/UserState.java
index 952283e..ff8014c 100644
--- a/services/core/java/com/android/server/am/UserState.java
+++ b/services/core/java/com/android/server/am/UserState.java
@@ -54,6 +54,7 @@
public int state = STATE_BOOTING;
public int lastState = STATE_BOOTING;
public boolean switching;
+ public boolean tokenProvided;
/**
* The last time that a provider was reported to usage stats as being brought to important
diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java
index 10e88e6..7022856 100644
--- a/services/core/java/com/android/server/am/UserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java
@@ -68,6 +68,12 @@
String viewMessage;
if (UserManager.isSplitSystemUser() && newUser.id == UserHandle.USER_SYSTEM) {
viewMessage = res.getString(R.string.user_logging_out_message, oldUser.name);
+ } else if (UserManager.isDeviceInDemoMode(context)) {
+ if (oldUser.isDemo()) {
+ viewMessage = res.getString(R.string.demo_restarting_message);
+ } else {
+ viewMessage = res.getString(R.string.demo_starting_message);
+ }
} else {
viewMessage = res.getString(R.string.user_switching_message, newUser.name);
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 7777ae23f..275870e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1130,8 +1130,11 @@
final int currentUser = getCurrentUserId();
// Check the current user restriction.
- boolean masterMute = mUserManagerInternal.getUserRestriction(
- currentUser, UserManager.DISALLOW_ADJUST_VOLUME);
+ boolean masterMute =
+ mUserManagerInternal.getUserRestriction(currentUser,
+ UserManager.DISALLLOW_UNMUTE_DEVICE)
+ || mUserManagerInternal.getUserRestriction(currentUser,
+ UserManager.DISALLOW_ADJUST_VOLUME);
if (mUseFixedVolume) {
masterMute = false;
AudioSystem.setMasterVolume(1.0f);
@@ -5380,9 +5383,11 @@
// Update speaker mute state.
{
final boolean wasRestricted =
- prevRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME);
+ prevRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME)
+ || prevRestrictions.getBoolean(UserManager.DISALLLOW_UNMUTE_DEVICE);
final boolean isRestricted =
- newRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME);
+ newRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME)
+ || newRestrictions.getBoolean(UserManager.DISALLLOW_UNMUTE_DEVICE);
if (wasRestricted != isRestricted) {
setMasterMuteInternalNoCallerCheck(isRestricted, /* flags =*/ 0, userId);
}
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 1c26846..66aa403 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -188,6 +188,14 @@
if (!canCopy) {
clip = null;
} else {
+ // We want to fix the uris of the related user's clip without changing the
+ // uris of the current user's clip.
+ // So, copy the ClipData, and then copy all the items, so that nothing
+ // is shared in memmory.
+ clip = new ClipData(clip);
+ for (int i = clip.getItemCount() - 1; i >= 0; i--) {
+ clip.setItemAt(i, new ClipData.Item(clip.getItemAt(i)));
+ }
clip.fixUrisLight(userId);
}
for (int i = 0; i < size; i++) {
diff --git a/services/core/java/com/android/server/connectivity/DnsEventListenerService.java b/services/core/java/com/android/server/connectivity/DnsEventListenerService.java
index 18ab731..8d206ef 100644
--- a/services/core/java/com/android/server/connectivity/DnsEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/DnsEventListenerService.java
@@ -17,15 +17,17 @@
package com.android.server.connectivity;
import android.content.Context;
-import android.net.metrics.DnsEvent;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkRequest;
+import android.net.metrics.DnsEvent;
import android.net.metrics.IDnsEventListener;
+import android.net.metrics.IpConnectivityLog;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import java.io.PrintWriter;
@@ -45,12 +47,13 @@
private static final boolean DBG = true;
private static final boolean VDBG = false;
+ // TODO: read this constant from system property
private static final int MAX_LOOKUPS_PER_DNS_EVENT = 100;
// Stores the results of a number of consecutive DNS lookups on the same network.
// This class is not thread-safe and it is the responsibility of the service to call its methods
// on one thread at a time.
- private static class DnsEventBatch {
+ private class DnsEventBatch {
private final int mNetId;
private final byte[] mEventTypes = new byte[MAX_LOOKUPS_PER_DNS_EVENT];
@@ -82,7 +85,7 @@
byte[] eventTypes = Arrays.copyOf(mEventTypes, mEventCount);
byte[] returnCodes = Arrays.copyOf(mReturnCodes, mEventCount);
int[] latenciesMs = Arrays.copyOf(mLatenciesMs, mEventCount);
- DnsEvent.logEvent(mNetId, eventTypes, returnCodes, latenciesMs);
+ mMetricsLog.log(new DnsEvent(mNetId, eventTypes, returnCodes, latenciesMs));
maybeLog(String.format("Logging %d results for netId %d", mEventCount, mNetId));
mEventCount = 0;
}
@@ -96,13 +99,14 @@
// Only sorted for ease of debugging. Because we only typically have a handful of networks up
// at any given time, performance is not a concern.
@GuardedBy("this")
- private SortedMap<Integer, DnsEventBatch> mEventBatches = new TreeMap<>();
+ private final SortedMap<Integer, DnsEventBatch> mEventBatches = new TreeMap<>();
// We register a NetworkCallback to ensure that when a network disconnects, we flush the DNS
// queries we've logged on that network. Because we do not do this periodically, we might lose
// up to MAX_LOOKUPS_PER_DNS_EVENT lookup stats on each network when the system is shutting
// down. We believe this to be sufficient for now.
private final ConnectivityManager mCm;
+ private final IpConnectivityLog mMetricsLog;
private final NetworkCallback mNetworkCallback = new NetworkCallback() {
@Override
public void onLost(Network network) {
@@ -116,11 +120,15 @@
};
public DnsEventListenerService(Context context) {
+ this(context.getSystemService(ConnectivityManager.class), new IpConnectivityLog());
+ }
+
+ @VisibleForTesting
+ public DnsEventListenerService(ConnectivityManager cm, IpConnectivityLog log) {
// We are started when boot is complete, so ConnectivityService should already be running.
- final NetworkRequest request = new NetworkRequest.Builder()
- .clearCapabilities()
- .build();
- mCm = context.getSystemService(ConnectivityManager.class);
+ mCm = cm;
+ mMetricsLog = log;
+ final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
mCm.registerNetworkCallback(request, mNetworkCallback);
}
diff --git a/services/core/java/com/android/server/connectivity/LingerMonitor.java b/services/core/java/com/android/server/connectivity/LingerMonitor.java
new file mode 100644
index 0000000..4034877
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/LingerMonitor.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.app.PendingIntent;
+import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
+import android.net.Uri;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.SparseBooleanArray;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import com.android.internal.util.MessageUtils;
+import com.android.server.connectivity.NetworkNotificationManager;
+import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
+
+import static android.net.ConnectivityManager.NETID_UNSET;
+
+/**
+ * Class that monitors default network linger events and possibly notifies the user of network
+ * switches.
+ *
+ * This class is not thread-safe and all its methods must be called on the ConnectivityService
+ * handler thread.
+ */
+public class LingerMonitor {
+
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false;
+ private static final String TAG = LingerMonitor.class.getSimpleName();
+
+ private static final HashMap<String, Integer> sTransportNames = makeTransportToNameMap();
+ private static final Intent CELLULAR_SETTINGS = new Intent().setComponent(new ComponentName(
+ "com.android.settings", "com.android.settings.Settings$DataUsageSummaryActivity"));
+
+ private static final int NOTIFY_TYPE_NONE = 0;
+ private static final int NOTIFY_TYPE_NOTIFICATION = 1;
+ private static final int NOTIFY_TYPE_TOAST = 2;
+
+ private static SparseArray<String> sNotifyTypeNames = MessageUtils.findMessageNames(
+ new Class[] { LingerMonitor.class }, new String[]{ "NOTIFY_TYPE_" });
+
+ private final Context mContext;
+ private final NetworkNotificationManager mNotifier;
+
+ /** Current notifications. Maps the netId we switched away from to the netId we switched to. */
+ private final SparseIntArray mNotifications = new SparseIntArray();
+
+ /** Whether we ever notified that we switched away from a particular network. */
+ private final SparseBooleanArray mEverNotified = new SparseBooleanArray();
+
+ public LingerMonitor(Context context, NetworkNotificationManager notifier) {
+ mContext = context;
+ mNotifier = notifier;
+ }
+
+ private static HashMap<String, Integer> makeTransportToNameMap() {
+ SparseArray<String> numberToName = MessageUtils.findMessageNames(
+ new Class[] { NetworkCapabilities.class }, new String[]{ "TRANSPORT_" });
+ HashMap<String, Integer> nameToNumber = new HashMap<>();
+ for (int i = 0; i < numberToName.size(); i++) {
+ // MessageUtils will fail to initialize if there are duplicate constant values, so there
+ // are no duplicates here.
+ nameToNumber.put(numberToName.valueAt(i), numberToName.keyAt(i));
+ }
+ return nameToNumber;
+ }
+
+ private static boolean hasTransport(NetworkAgentInfo nai, int transport) {
+ return nai.networkCapabilities.hasTransport(transport);
+ }
+
+ private int getNotificationSource(NetworkAgentInfo toNai) {
+ for (int i = 0; i < mNotifications.size(); i++) {
+ if (mNotifications.valueAt(i) == toNai.network.netId) {
+ return mNotifications.keyAt(i);
+ }
+ }
+ return NETID_UNSET;
+ }
+
+ private boolean everNotified(NetworkAgentInfo nai) {
+ return mEverNotified.get(nai.network.netId, false);
+ }
+
+ private boolean isNotificationEnabled(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) {
+ // TODO: Evaluate moving to CarrierConfigManager.
+ String[] notifySwitches = mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_networkNotifySwitches);
+
+ if (VDBG) {
+ Log.d(TAG, "Notify on network switches: " + Arrays.toString(notifySwitches));
+ }
+
+ for (String notifySwitch : notifySwitches) {
+ if (TextUtils.isEmpty(notifySwitch)) continue;
+ String[] transports = notifySwitch.split("-", 2);
+ if (transports.length != 2) {
+ Log.e(TAG, "Invalid network switch notification configuration: " + notifySwitch);
+ continue;
+ }
+ int fromTransport = sTransportNames.get("TRANSPORT_" + transports[0]);
+ int toTransport = sTransportNames.get("TRANSPORT_" + transports[1]);
+ if (hasTransport(fromNai, fromTransport) && hasTransport(toNai, toTransport)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void showNotification(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) {
+ PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
+ mContext, 0, CELLULAR_SETTINGS, PendingIntent.FLAG_CANCEL_CURRENT, null,
+ UserHandle.CURRENT);
+
+ mNotifier.showNotification(fromNai.network.netId, NotificationType.NETWORK_SWITCH,
+ fromNai, toNai, pendingIntent, true);
+ }
+
+ // Removes any notification that was put up as a result of switching to nai.
+ private void maybeStopNotifying(NetworkAgentInfo nai) {
+ int fromNetId = getNotificationSource(nai);
+ if (fromNetId != NETID_UNSET) {
+ mNotifications.delete(fromNetId);
+ mNotifier.clearNotification(fromNetId);
+ // Toasts can't be deleted.
+ }
+ }
+
+ // Notify the user of a network switch using a notification or a toast.
+ private void notify(NetworkAgentInfo fromNai, NetworkAgentInfo toNai, boolean forceToast) {
+ boolean notify = false;
+ int notifyType = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_networkNotifySwitchType);
+
+ if (notifyType == NOTIFY_TYPE_NOTIFICATION && forceToast) {
+ notifyType = NOTIFY_TYPE_TOAST;
+ }
+
+ switch (notifyType) {
+ case NOTIFY_TYPE_NONE:
+ break;
+ case NOTIFY_TYPE_NOTIFICATION:
+ showNotification(fromNai, toNai);
+ notify = true;
+ break;
+ case NOTIFY_TYPE_TOAST:
+ mNotifier.showToast(fromNai, toNai);
+ notify = true;
+ break;
+ default:
+ Log.e(TAG, "Unknown notify type " + notifyType);
+ }
+
+ if (VDBG) {
+ Log.d(TAG, "Notify type: " + sNotifyTypeNames.get(notifyType, "" + notifyType));
+ }
+
+ if (notify) {
+ if (DBG) {
+ Log.d(TAG, "Notifying switch from=" + fromNai.name() + " to=" + toNai.name() +
+ " type=" + sNotifyTypeNames.get(notifyType, "unknown(" + notifyType + ")"));
+ }
+ mNotifications.put(fromNai.network.netId, toNai.network.netId);
+ mEverNotified.put(fromNai.network.netId, true);
+ }
+ }
+
+ // The default network changed from fromNai to toNai due to a change in score.
+ public void noteLingerDefaultNetwork(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) {
+ if (VDBG) {
+ Log.d(TAG, "noteLingerDefaultNetwork from=" + fromNai.name() +
+ " everValidated=" + fromNai.everValidated +
+ " lastValidated=" + fromNai.lastValidated +
+ " to=" + toNai.name());
+ }
+
+ // If we are currently notifying the user because the device switched to fromNai, now that
+ // we are switching away from it we should remove the notification. This includes the case
+ // where we switch back to toNai because its score improved again (e.g., because it regained
+ // Internet access).
+ maybeStopNotifying(fromNai);
+
+ // If this network never validated, don't notify. Otherwise, we could do things like:
+ //
+ // 1. Unvalidated wifi connects.
+ // 2. Unvalidated mobile data connects.
+ // 3. Cell validates, and we show a notification.
+ // or:
+ // 1. User connects to wireless printer.
+ // 2. User turns on cellular data.
+ // 3. We show a notification.
+ if (!fromNai.everValidated) return;
+
+ // If this network is a captive portal, don't notify. This cannot happen on initial connect
+ // to a captive portal, because the everValidated check above will fail. However, it can
+ // happen if the captive portal reasserts itself (e.g., because its timeout fires). In that
+ // case, as soon as the captive portal reasserts itself, we'll show a sign-in notification.
+ // We don't want to overwrite that notification with this one; the user has already been
+ // notified, and of the two, the captive portal notification is the more useful one because
+ // it allows the user to sign in to the captive portal. In this case, display a toast
+ // in addition to the captive portal notification.
+ //
+ // Note that if the network we switch to is already up when the captive portal reappears,
+ // this won't work because NetworkMonitor tells ConnectivityService that the network is
+ // unvalidated (causing a switch) before asking it to show the sign in notification. In this
+ // case, the toast won't show and we'll only display the sign in notification. This is the
+ // best we can do at this time.
+ boolean forceToast = fromNai.networkCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
+
+ // Only show the notification once, in order to avoid irritating the user every time.
+ // TODO: should we do this?
+ if (everNotified(fromNai)) {
+ if (VDBG) {
+ Log.d(TAG, "Not notifying handover from " + fromNai.name() + ", already notified");
+ }
+ return;
+ }
+
+ if (isNotificationEnabled(fromNai, toNai)) {
+ notify(fromNai, toNai, forceToast);
+ }
+ }
+
+ public void noteDisconnect(NetworkAgentInfo nai) {
+ mNotifications.delete(nai.network.netId);
+ mEverNotified.delete(nai.network.netId);
+ maybeStopNotifying(nai);
+ // No need to cancel notifications on nai: NetworkMonitor does that on disconnect.
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
index 69ef30f..05f1a6e 100644
--- a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
+++ b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
@@ -16,6 +16,7 @@
package com.android.server.connectivity;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.SystemService;
import android.app.PendingIntent;
@@ -60,17 +61,11 @@
}
}
- // TODO: read from system property
- private final int MAX_NUMBER_OF_EVENTS = 1000;
-
- // TODO: read from system property
- private final int EVENTS_NOTIFICATION_THRESHOLD = 300;
-
- // TODO: read from system property
- private final int THROTTLING_TIME_INTERVAL_MILLIS = 60 * 60 * 1000; // 1 hour
-
- // TODO: read from system property
+ // TODO: read these constants from system property
+ private final int EVENTS_NOTIFICATION_THRESHOLD = 300;
+ private final int MAX_NUMBER_OF_EVENTS = 1000;
private final int THROTTLING_MAX_NUMBER_OF_MESSAGES_PER_COMPONENT = 1000;
+ private final long THROTTLING_TIME_INTERVAL_MILLIS = DateUtils.HOUR_IN_MILLIS;
private int mEventCounter = 0;
@@ -127,10 +122,13 @@
mEvents.addLast(e);
}
+ @VisibleForTesting
+ final MetricsLoggerImpl mBinder = new MetricsLoggerImpl();
+
/**
* Implementation of the IConnectivityMetricsLogger interface.
*/
- private final IConnectivityMetricsLogger.Stub mBinder = new IConnectivityMetricsLogger.Stub() {
+ final class MetricsLoggerImpl extends IConnectivityMetricsLogger.Stub {
private final ArrayList<PendingIntent> mPendingIntents = new ArrayList<>();
@@ -223,7 +221,9 @@
}
pw.println();
- mDnsListener.dump(pw);
+ if (mDnsListener != null) {
+ mDnsListener.dump(pw);
+ }
}
public long logEvent(ConnectivityMetricsEvent event) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index d487bd0..b0330b9 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -28,14 +28,21 @@
import android.net.NetworkState;
import android.os.Handler;
import android.os.Messenger;
+import android.os.SystemClock;
+import android.util.Log;
import android.util.SparseArray;
import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.WakeupMessage;
import com.android.server.ConnectivityService;
import com.android.server.connectivity.NetworkMonitor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.Objects;
+import java.util.SortedSet;
+import java.util.TreeSet;
/**
* A bag class used by ConnectivityService for holding a collection of most recent
@@ -143,12 +150,69 @@
// Whether a captive portal was found during the last network validation attempt.
public boolean lastCaptivePortalDetected;
- // Indicates whether the network is lingering. Networks are lingered when they become unneeded
- // as a result of their NetworkRequests being satisfied by a different network, so as to allow
- // communication to wrap up before the network is taken down. This usually only happens to the
- // default network. Lingering ends with either the linger timeout expiring and the network
- // being taken down, or the network satisfying a request again.
- public boolean lingering;
+ // Networks are lingered when they become unneeded as a result of their NetworkRequests being
+ // satisfied by a higher-scoring network. so as to allow communication to wrap up before the
+ // network is taken down. This usually only happens to the default network. Lingering ends with
+ // either the linger timeout expiring and the network being taken down, or the network
+ // satisfying a request again.
+ public static class LingerTimer implements Comparable<LingerTimer> {
+ public final NetworkRequest request;
+ public final long expiryMs;
+
+ public LingerTimer(NetworkRequest request, long expiryMs) {
+ this.request = request;
+ this.expiryMs = expiryMs;
+ }
+ public boolean equals(Object o) {
+ if (!(o instanceof LingerTimer)) return false;
+ LingerTimer other = (LingerTimer) o;
+ return (request.requestId == other.request.requestId) && (expiryMs == other.expiryMs);
+ }
+ public int hashCode() {
+ return Objects.hash(request.requestId, expiryMs);
+ }
+ public int compareTo(LingerTimer other) {
+ return (expiryMs != other.expiryMs) ?
+ Long.compare(expiryMs, other.expiryMs) :
+ Integer.compare(request.requestId, other.request.requestId);
+ }
+ public String toString() {
+ return String.format("%s, expires %dms", request.toString(),
+ expiryMs - SystemClock.elapsedRealtime());
+ }
+ }
+
+ /**
+ * Inform ConnectivityService that the network LINGER period has
+ * expired.
+ * obj = this NetworkAgentInfo
+ */
+ public static final int EVENT_NETWORK_LINGER_COMPLETE = 1001;
+
+ // All linger timers for this network, sorted by expiry time. A linger timer is added whenever
+ // a request is moved to a network with a better score, regardless of whether the network is or
+ // was lingering or not.
+ // TODO: determine if we can replace this with a smaller or unsorted data structure. (e.g.,
+ // SparseLongArray) combined with the timestamp of when the last timer is scheduled to fire.
+ private final SortedSet<LingerTimer> mLingerTimers = new TreeSet<>();
+
+ // For fast lookups. Indexes into mLingerTimers by request ID.
+ private final SparseArray<LingerTimer> mLingerTimerForRequest = new SparseArray<>();
+
+ // Linger expiry timer. Armed whenever mLingerTimers is non-empty, regardless of whether the
+ // network is lingering or not. Always set to the expiry of the LingerTimer that expires last.
+ // When the timer fires, all linger state is cleared, and if the network has no requests, it is
+ // torn down.
+ private WakeupMessage mLingerMessage;
+
+ // Linger expiry. Holds the expiry time of the linger timer, or 0 if the timer is not armed.
+ private long mLingerExpiryMs;
+
+ // Whether the network is lingering or not. Must be maintained separately from the above because
+ // it depends on the state of other networks and requests, which only ConnectivityService knows.
+ // (Example: we don't linger a network if it would become the best for a NetworkRequest if it
+ // validated).
+ private boolean mLingering;
// This represents the last score received from the NetworkAgent.
private int currentScore;
@@ -162,11 +226,11 @@
private static final int MAXIMUM_NETWORK_SCORE = 100;
// The list of NetworkRequests being satisfied by this Network.
- public final SparseArray<NetworkRequest> networkRequests = new SparseArray<NetworkRequest>();
+ private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>();
// The list of NetworkRequests that this Network previously satisfied with the highest
// score. A non-empty list indicates that if this Network was validated it is lingered.
- // NOTE: This list is only used for debugging.
- public final ArrayList<NetworkRequest> networkLingered = new ArrayList<NetworkRequest>();
+ // How many of the satisfied requests are actual requests and not listens.
+ private int mNumRequestNetworkRequests = 0;
public final Messenger messenger;
public final AsyncChannel asyncChannel;
@@ -174,6 +238,12 @@
// Used by ConnectivityService to keep track of 464xlat.
public Nat464Xlat clatd;
+ private static final String TAG = ConnectivityService.class.getSimpleName();
+ private static final boolean VDBG = false;
+ private final ConnectivityService mConnService;
+ private final Context mContext;
+ private final Handler mHandler;
+
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
NetworkMisc misc, NetworkRequest defaultRequest, ConnectivityService connService) {
@@ -184,22 +254,74 @@
linkProperties = lp;
networkCapabilities = nc;
currentScore = score;
- networkMonitor = connService.createNetworkMonitor(context, handler, this, defaultRequest);
+ mConnService = connService;
+ mContext = context;
+ mHandler = handler;
+ networkMonitor = mConnService.createNetworkMonitor(context, handler, this, defaultRequest);
networkMisc = misc;
}
+ // Functions for manipulating the requests satisfied by this network.
+ //
+ // These functions must only called on ConnectivityService's main thread.
+
/**
* Add {@code networkRequest} to this network as it's satisfied by this network.
- * NOTE: This function must only be called on ConnectivityService's main thread.
* @return true if {@code networkRequest} was added or false if {@code networkRequest} was
* already present.
*/
public boolean addRequest(NetworkRequest networkRequest) {
- if (networkRequests.get(networkRequest.requestId) == networkRequest) return false;
- networkRequests.put(networkRequest.requestId, networkRequest);
+ NetworkRequest existing = mNetworkRequests.get(networkRequest.requestId);
+ if (existing == networkRequest) return false;
+ if (existing != null && existing.isRequest()) mNumRequestNetworkRequests--;
+ mNetworkRequests.put(networkRequest.requestId, networkRequest);
+ if (networkRequest.isRequest()) mNumRequestNetworkRequests++;
return true;
}
+ /**
+ * Remove the specified request from this network.
+ */
+ public void removeRequest(int requestId) {
+ NetworkRequest existing = mNetworkRequests.get(requestId);
+ if (existing == null) return;
+ mNetworkRequests.remove(requestId);
+ if (existing.isRequest()) {
+ mNumRequestNetworkRequests--;
+ unlingerRequest(existing);
+ }
+ }
+
+ /**
+ * Returns whether this network is currently satisfying the request with the specified ID.
+ */
+ public boolean isSatisfyingRequest(int id) {
+ return mNetworkRequests.get(id) != null;
+ }
+
+ /**
+ * Returns the request at the specified position in the list of requests satisfied by this
+ * network.
+ */
+ public NetworkRequest requestAt(int index) {
+ return mNetworkRequests.valueAt(index);
+ }
+
+ /**
+ * Returns the number of requests currently satisfied by this network for which
+ * {@link android.net.NetworkRequest#isRequest} returns {@code true}.
+ */
+ public int numRequestNetworkRequests() {
+ return mNumRequestNetworkRequests;
+ }
+
+ /**
+ * Returns the number of requests of any type currently satisfied by this network.
+ */
+ public int numNetworkRequests() {
+ return mNetworkRequests.size();
+ }
+
// Does this network satisfy request?
public boolean satisfies(NetworkRequest request) {
return created &&
@@ -269,13 +391,103 @@
}
}
+ /**
+ * Sets the specified request to linger on this network for the specified time. Called by
+ * ConnectivityService when the request is moved to another network with a higher score.
+ */
+ public void lingerRequest(NetworkRequest request, long now, long duration) {
+ if (mLingerTimerForRequest.get(request.requestId) != null) {
+ // Cannot happen. Once a request is lingering on a particular network, we cannot
+ // re-linger it unless that network becomes the best for that request again, in which
+ // case we should have unlingered it.
+ Log.wtf(TAG, this.name() + ": request " + request.requestId + " already lingered");
+ }
+ final long expiryMs = now + duration;
+ LingerTimer timer = new LingerTimer(request, expiryMs);
+ if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + this.name());
+ mLingerTimers.add(timer);
+ mLingerTimerForRequest.put(request.requestId, timer);
+ }
+
+ /**
+ * Cancel lingering. Called by ConnectivityService when a request is added to this network.
+ * Returns true if the given request was lingering on this network, false otherwise.
+ */
+ public boolean unlingerRequest(NetworkRequest request) {
+ LingerTimer timer = mLingerTimerForRequest.get(request.requestId);
+ if (timer != null) {
+ if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + this.name());
+ mLingerTimers.remove(timer);
+ mLingerTimerForRequest.remove(request.requestId);
+ return true;
+ }
+ return false;
+ }
+
+ public long getLingerExpiry() {
+ return mLingerExpiryMs;
+ }
+
+ public void updateLingerTimer() {
+ long newExpiry = mLingerTimers.isEmpty() ? 0 : mLingerTimers.last().expiryMs;
+ if (newExpiry == mLingerExpiryMs) return;
+
+ // Even if we're going to reschedule the timer, cancel it first. This is because the
+ // semantics of WakeupMessage guarantee that if cancel is called then the alarm will
+ // never call its callback (handleLingerComplete), even if it has already fired.
+ // WakeupMessage makes no such guarantees about rescheduling a message, so if mLingerMessage
+ // has already been dispatched, rescheduling to some time in the future it won't stop it
+ // from calling its callback immediately.
+ if (mLingerMessage != null) {
+ mLingerMessage.cancel();
+ mLingerMessage = null;
+ }
+
+ if (newExpiry > 0) {
+ mLingerMessage = mConnService.makeWakeupMessage(
+ mContext, mHandler,
+ "NETWORK_LINGER_COMPLETE." + network.netId,
+ EVENT_NETWORK_LINGER_COMPLETE, this);
+ mLingerMessage.schedule(newExpiry);
+ }
+
+ mLingerExpiryMs = newExpiry;
+ }
+
+ public void linger() {
+ mLingering = true;
+ }
+
+ public void unlinger() {
+ mLingering = false;
+ }
+
+ public boolean isLingering() {
+ return mLingering;
+ }
+
+ public void clearLingerState() {
+ if (mLingerMessage != null) {
+ mLingerMessage.cancel();
+ mLingerMessage = null;
+ }
+ mLingerTimers.clear();
+ mLingerTimerForRequest.clear();
+ updateLingerTimer(); // Sets mLingerExpiryMs, cancels and nulls out mLingerMessage.
+ mLingering = false;
+ }
+
+ public void dumpLingerTimers(PrintWriter pw) {
+ for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
+ }
+
public String toString() {
return "NetworkAgentInfo{ ni{" + networkInfo + "} " +
"network{" + network + "} nethandle{" + network.getNetworkHandle() + "} " +
"lp{" + linkProperties + "} " +
"nc{" + networkCapabilities + "} Score{" + getCurrentScore() + "} " +
"everValidated{" + everValidated + "} lastValidated{" + lastValidated + "} " +
- "created{" + created + "} lingering{" + lingering + "} " +
+ "created{" + created + "} lingering{" + isLingering() + "} " +
"explicitlySelected{" + networkMisc.explicitlySelected + "} " +
"acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} " +
"everCaptivePortalDetected{" + everCaptivePortalDetected + "} " +
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index f4e1424..42d80fc 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -34,8 +34,9 @@
import android.net.ProxyInfo;
import android.net.TrafficStats;
import android.net.Uri;
-import android.net.metrics.ValidationProbeEvent;
+import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
+import android.net.metrics.ValidationProbeEvent;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.util.Stopwatch;
@@ -66,7 +67,6 @@
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.internal.util.WakeupMessage;
-import com.android.server.connectivity.NetworkAgentInfo;
import java.io.IOException;
import java.net.HttpURLConnection;
@@ -132,31 +132,6 @@
public static final int EVENT_NETWORK_TESTED = BASE + 2;
/**
- * Inform NetworkMonitor to linger a network. The Monitor should
- * start a timer and/or start watching for zero live connections while
- * moving towards LINGER_COMPLETE. After the Linger period expires
- * (or other events mark the end of the linger state) the LINGER_COMPLETE
- * event should be sent and the network will be shut down. If a
- * CMD_NETWORK_CONNECTED happens before the LINGER completes
- * it indicates further desire to keep the network alive and so
- * the LINGER is aborted.
- */
- public static final int CMD_NETWORK_LINGER = BASE + 3;
-
- /**
- * Message to self indicating linger delay has expired.
- * arg1 = Token to ignore old messages.
- */
- private static final int CMD_LINGER_EXPIRED = BASE + 4;
-
- /**
- * Inform ConnectivityService that the network LINGER period has
- * expired.
- * obj = NetworkAgentInfo
- */
- public static final int EVENT_NETWORK_LINGER_COMPLETE = BASE + 5;
-
- /**
* Message to self indicating it's time to evaluate a network's connectivity.
* arg1 = Token to ignore old messages.
*/
@@ -204,12 +179,6 @@
*/
private static final int CMD_CAPTIVE_PORTAL_RECHECK = BASE + 12;
- private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
- // Default to 30s linger time-out. Modifyable only for testing.
- private static int DEFAULT_LINGER_DELAY_MS = 30000;
- private final int mLingerDelayMs;
- private int mLingerToken = 0;
-
// Start mReevaluateDelayMs at this value and double.
private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
private static final int MAX_REEVALUATE_DELAY_MS = 10*60*1000;
@@ -231,6 +200,7 @@
private final WifiManager mWifiManager;
private final AlarmManager mAlarmManager;
private final NetworkRequest mDefaultRequest;
+ private final IpConnectivityLog mMetricsLog;
private boolean mIsCaptivePortalCheckEnabled;
private boolean mUseHttps;
@@ -247,7 +217,6 @@
private final State mMaybeNotifyState = new MaybeNotifyState();
private final State mEvaluatingState = new EvaluatingState();
private final State mCaptivePortalState = new CaptivePortalState();
- private final State mLingeringState = new LingeringState();
private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null;
@@ -257,10 +226,17 @@
public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
NetworkRequest defaultRequest) {
+ this(context, handler, networkAgentInfo, defaultRequest, new IpConnectivityLog());
+ }
+
+ @VisibleForTesting
+ protected NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
+ NetworkRequest defaultRequest, IpConnectivityLog logger) {
// Add suffix indicating which NetworkMonitor we're talking about.
super(TAG + networkAgentInfo.name());
mContext = context;
+ mMetricsLog = logger;
mConnectivityServiceHandler = handler;
mNetworkAgentInfo = networkAgentInfo;
mNetId = mNetworkAgentInfo.network.netId;
@@ -274,11 +250,8 @@
addState(mMaybeNotifyState, mDefaultState);
addState(mEvaluatingState, mMaybeNotifyState);
addState(mCaptivePortalState, mMaybeNotifyState);
- addState(mLingeringState, mDefaultState);
setInitialState(mDefaultState);
- mLingerDelayMs = SystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
-
mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1;
mUseHttps = Settings.Global.getInt(mContext.getContentResolver(),
@@ -307,16 +280,12 @@
@Override
public boolean processMessage(Message message) {
switch (message.what) {
- case CMD_NETWORK_LINGER:
- log("Lingering");
- transitionTo(mLingeringState);
- return HANDLED;
case CMD_NETWORK_CONNECTED:
- NetworkEvent.logEvent(mNetId, NetworkEvent.NETWORK_CONNECTED);
+ logNetworkEvent(NetworkEvent.NETWORK_CONNECTED);
transitionTo(mEvaluatingState);
return HANDLED;
case CMD_NETWORK_DISCONNECTED:
- NetworkEvent.logEvent(mNetId, NetworkEvent.NETWORK_DISCONNECTED);
+ logNetworkEvent(NetworkEvent.NETWORK_DISCONNECTED);
if (mLaunchCaptivePortalAppBroadcastReceiver != null) {
mContext.unregisterReceiver(mLaunchCaptivePortalAppBroadcastReceiver);
mLaunchCaptivePortalAppBroadcastReceiver = null;
@@ -381,10 +350,7 @@
private class ValidatedState extends State {
@Override
public void enter() {
- if (mEvaluationTimer.isRunning()) {
- NetworkEvent.logValidated(mNetId, mEvaluationTimer.stop());
- mEvaluationTimer.reset();
- }
+ maybeLogEvaluationResult(NetworkEvent.NETWORK_VALIDATED);
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
NETWORK_TEST_RESULT_VALID, mNetworkAgentInfo.network.netId, null));
}
@@ -531,7 +497,7 @@
} else {
final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
sendMessageDelayed(msg, mReevaluateDelayMs);
- NetworkEvent.logEvent(mNetId, NetworkEvent.NETWORK_VALIDATION_FAILED);
+ logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED);
mConnectivityServiceHandler.sendMessage(obtainMessage(
EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, mNetId,
probeResult.mRedirectUrl));
@@ -591,10 +557,7 @@
@Override
public void enter() {
- if (mEvaluationTimer.isRunning()) {
- NetworkEvent.logCaptivePortalFound(mNetId, mEvaluationTimer.stop());
- mEvaluationTimer.reset();
- }
+ maybeLogEvaluationResult(NetworkEvent.NETWORK_CAPTIVE_PORTAL_FOUND);
// Don't annoy user with sign-in notifications.
if (mDontDisplaySigninNotification) return;
// Create a CustomIntentReceiver that sends us a
@@ -618,73 +581,7 @@
@Override
public void exit() {
- removeMessages(CMD_CAPTIVE_PORTAL_RECHECK);
- }
- }
-
- // Being in the LingeringState State indicates a Network's validated bit is true and it once
- // was the highest scoring Network satisfying a particular NetworkRequest, but since then
- // another Network satisfied the NetworkRequest with a higher score and hence this Network
- // is "lingered" for a fixed period of time before it is disconnected. This period of time
- // allows apps to wrap up communication and allows for seamless reactivation if the other
- // higher scoring Network happens to disconnect.
- private class LingeringState extends State {
- private static final String ACTION_LINGER_EXPIRED = "android.net.netmon.lingerExpired";
-
- private WakeupMessage mWakeupMessage;
-
- @Override
- public void enter() {
- mEvaluationTimer.reset();
- final String cmdName = ACTION_LINGER_EXPIRED + "." + mNetId;
- mWakeupMessage = makeWakeupMessage(mContext, getHandler(), cmdName, CMD_LINGER_EXPIRED);
- long wakeupTime = SystemClock.elapsedRealtime() + mLingerDelayMs;
- mWakeupMessage.schedule(wakeupTime);
- }
-
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_NETWORK_CONNECTED:
- log("Unlingered");
- // If already validated, go straight to validated state.
- if (mNetworkAgentInfo.lastValidated) {
- transitionTo(mValidatedState);
- return HANDLED;
- }
- return NOT_HANDLED;
- case CMD_LINGER_EXPIRED:
- mConnectivityServiceHandler.sendMessage(
- obtainMessage(EVENT_NETWORK_LINGER_COMPLETE, mNetworkAgentInfo));
- return HANDLED;
- case CMD_FORCE_REEVALUATION:
- // Ignore reevaluation attempts when lingering. A reevaluation could result
- // in a transition to the validated state which would abort the linger
- // timeout. Lingering is the result of score assessment; validity is
- // irrelevant.
- return HANDLED;
- case CMD_CAPTIVE_PORTAL_APP_FINISHED:
- // Ignore user network determination as this could abort linger timeout.
- // Networks are only lingered once validated because:
- // - Unvalidated networks are never lingered (see rematchNetworkAndRequests).
- // - Once validated, a Network's validated bit is never cleared.
- // Since networks are only lingered after being validated a user's
- // determination will not change the death sentence that lingering entails:
- // - If the user wants to use the network or bypasses the captive portal,
- // the network's score will not be increased beyond its current value
- // because it is already validated. Without a score increase there is no
- // chance of reactivation (i.e. aborting linger timeout).
- // - If the user does not want the network, lingering will disconnect the
- // network anyhow.
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- @Override
- public void exit() {
- mWakeupMessage.cancel();
+ removeMessages(CMD_CAPTIVE_PORTAL_RECHECK);
}
}
@@ -759,11 +656,12 @@
if (!TextUtils.isEmpty(hostToResolve)) {
String probeName = ValidationProbeEvent.getProbeName(ValidationProbeEvent.PROBE_DNS);
final Stopwatch dnsTimer = new Stopwatch().start();
+ int dnsResult;
+ long dnsLatency;
try {
InetAddress[] addresses = mNetworkAgentInfo.network.getAllByName(hostToResolve);
- long dnsLatency = dnsTimer.stop();
- ValidationProbeEvent.logEvent(mNetId, dnsLatency,
- ValidationProbeEvent.PROBE_DNS, ValidationProbeEvent.DNS_SUCCESS);
+ dnsResult = ValidationProbeEvent.DNS_SUCCESS;
+ dnsLatency = dnsTimer.stop();
final StringBuffer connectInfo = new StringBuffer(", " + hostToResolve + "=");
for (InetAddress address : addresses) {
connectInfo.append(address.getHostAddress());
@@ -771,11 +669,11 @@
}
validationLog(probeName + " OK " + dnsLatency + "ms" + connectInfo);
} catch (UnknownHostException e) {
- long dnsLatency = dnsTimer.stop();
- ValidationProbeEvent.logEvent(mNetId, dnsLatency,
- ValidationProbeEvent.PROBE_DNS, ValidationProbeEvent.DNS_FAILURE);
+ dnsResult = ValidationProbeEvent.DNS_FAILURE;
+ dnsLatency = dnsTimer.stop();
validationLog(probeName + " FAIL " + dnsLatency + "ms, " + hostToResolve);
}
+ logValidationProbe(dnsLatency, ValidationProbeEvent.PROBE_DNS, dnsResult);
}
CaptivePortalProbeResult result;
@@ -856,7 +754,7 @@
urlConnection.disconnect();
}
}
- ValidationProbeEvent.logEvent(mNetId, probeTimer.stop(), probeType, httpResponseCode);
+ logValidationProbe(probeTimer.stop(), probeType, httpResponseCode);
return new CaptivePortalProbeResult(httpResponseCode, redirectUrl);
}
@@ -1000,17 +898,18 @@
PERMISSION_ACCESS_NETWORK_CONDITIONS);
}
- // Allow tests to override linger time.
- @VisibleForTesting
- public static void SetDefaultLingerTime(int time_ms) {
- if (Process.myUid() == Process.SYSTEM_UID) {
- throw new SecurityException("SetDefaultLingerTime only for internal testing.");
- }
- DEFAULT_LINGER_DELAY_MS = time_ms;
+ private void logNetworkEvent(int evtype) {
+ mMetricsLog.log(new NetworkEvent(mNetId, evtype));
}
- @VisibleForTesting
- protected WakeupMessage makeWakeupMessage(Context c, Handler h, String s, int i) {
- return new WakeupMessage(c, h, s, i);
+ private void maybeLogEvaluationResult(int evtype) {
+ if (mEvaluationTimer.isRunning()) {
+ mMetricsLog.log(new NetworkEvent(mNetId, evtype, mEvaluationTimer.stop()));
+ mEvaluationTimer.reset();
+ }
+ }
+
+ private void logValidationProbe(long durationMs, int probeType, int probeResult) {
+ mMetricsLog.log(new ValidationProbeEvent(mNetId, durationMs, probeType, probeResult));
}
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
new file mode 100644
index 0000000..99926a9
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.widget.Toast;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.NetworkCapabilities;
+import android.os.UserHandle;
+import android.telephony.TelephonyManager;
+import android.util.Slog;
+
+import com.android.internal.R;
+
+import static android.net.NetworkCapabilities.*;
+
+
+public class NetworkNotificationManager {
+
+ public static enum NotificationType { SIGN_IN, NO_INTERNET, NETWORK_SWITCH };
+
+ private static final String NOTIFICATION_ID = "Connectivity.Notification";
+
+ private static final String TAG = NetworkNotificationManager.class.getSimpleName();
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false;
+
+ private final Context mContext;
+ private final TelephonyManager mTelephonyManager;
+ private final NotificationManager mNotificationManager;
+
+ public NetworkNotificationManager(Context c, TelephonyManager t, NotificationManager n) {
+ mContext = c;
+ mTelephonyManager = t;
+ mNotificationManager = n;
+ }
+
+ // TODO: deal more gracefully with multi-transport networks.
+ private static int getFirstTransportType(NetworkAgentInfo nai) {
+ for (int i = 0; i < 64; i++) {
+ if (nai.networkCapabilities.hasTransport(i)) return i;
+ }
+ return -1;
+ }
+
+ private static String getTransportName(int transportType) {
+ Resources r = Resources.getSystem();
+ String[] networkTypes = r.getStringArray(R.array.network_switch_type_name);
+ try {
+ return networkTypes[transportType];
+ } catch (IndexOutOfBoundsException e) {
+ return r.getString(R.string.network_switch_type_name_unknown);
+ }
+ }
+
+ private static int getIcon(int transportType) {
+ return (transportType == TRANSPORT_WIFI) ?
+ R.drawable.stat_notify_wifi_in_range : // TODO: Distinguish ! from ?.
+ R.drawable.stat_notify_rssi_in_range;
+ }
+
+ /**
+ * Show or hide network provisioning notifications.
+ *
+ * We use notifications for two purposes: to notify that a network requires sign in
+ * (NotificationType.SIGN_IN), or to notify that a network does not have Internet access
+ * (NotificationType.NO_INTERNET). We display at most one notification per ID, so on a
+ * particular network we can display the notification type that was most recently requested.
+ * So for example if a captive portal fails to reply within a few seconds of connecting, we
+ * might first display NO_INTERNET, and then when the captive portal check completes, display
+ * SIGN_IN.
+ *
+ * @param id an identifier that uniquely identifies this notification. This must match
+ * between show and hide calls. We use the NetID value but for legacy callers
+ * we concatenate the range of types with the range of NetIDs.
+ * @param nai the network with which the notification is associated. For a SIGN_IN or
+ * NO_INTERNET notification, this is the network we're connecting to. For a
+ * NETWORK_SWITCH notification it's the network that we switched from. When this network
+ * disconnects the notification is removed.
+ * @param switchToNai for a NETWORK_SWITCH notification, the network we are switching to. Null
+ * in all other cases. Only used to determine the text of the notification.
+ */
+ public void showNotification(int id, NotificationType notifyType, NetworkAgentInfo nai,
+ NetworkAgentInfo switchToNai, PendingIntent intent, boolean highPriority) {
+ int transportType;
+ String extraInfo;
+ if (nai != null) {
+ transportType = getFirstTransportType(nai);
+ extraInfo = nai.networkInfo.getExtraInfo();
+ // Only notify for Internet-capable networks.
+ if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)) return;
+ } else {
+ // Legacy notifications.
+ transportType = TRANSPORT_CELLULAR;
+ extraInfo = null;
+ }
+
+ if (DBG) {
+ Slog.d(TAG, "showNotification " + notifyType
+ + " transportType=" + getTransportName(transportType)
+ + " extraInfo=" + extraInfo + " highPriority=" + highPriority);
+ }
+
+ Resources r = Resources.getSystem();
+ CharSequence title;
+ CharSequence details;
+ int icon = getIcon(transportType);
+ if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
+ title = r.getString(R.string.wifi_no_internet, 0);
+ details = r.getString(R.string.wifi_no_internet_detailed);
+ } else if (notifyType == NotificationType.SIGN_IN) {
+ switch (transportType) {
+ case TRANSPORT_WIFI:
+ title = r.getString(R.string.wifi_available_sign_in, 0);
+ details = r.getString(R.string.network_available_sign_in_detailed, extraInfo);
+ break;
+ case TRANSPORT_CELLULAR:
+ title = r.getString(R.string.network_available_sign_in, 0);
+ // TODO: Change this to pull from NetworkInfo once a printable
+ // name has been added to it
+ details = mTelephonyManager.getNetworkOperatorName();
+ break;
+ default:
+ title = r.getString(R.string.network_available_sign_in, 0);
+ details = r.getString(R.string.network_available_sign_in_detailed, extraInfo);
+ break;
+ }
+ } else if (notifyType == NotificationType.NETWORK_SWITCH) {
+ String fromTransport = getTransportName(transportType);
+ String toTransport = getTransportName(getFirstTransportType(switchToNai));
+ title = r.getString(R.string.network_switch_metered, toTransport);
+ details = r.getString(R.string.network_switch_metered_detail, toTransport,
+ fromTransport);
+ } else {
+ Slog.wtf(TAG, "Unknown notification type " + notifyType + "on network transport "
+ + getTransportName(transportType));
+ return;
+ }
+
+ Notification.Builder builder = new Notification.Builder(mContext)
+ .setWhen(System.currentTimeMillis())
+ .setShowWhen(notifyType == NotificationType.NETWORK_SWITCH)
+ .setSmallIcon(icon)
+ .setAutoCancel(true)
+ .setTicker(title)
+ .setColor(mContext.getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setContentTitle(title)
+ .setContentIntent(intent)
+ .setLocalOnly(true)
+ .setPriority(highPriority ?
+ Notification.PRIORITY_HIGH :
+ Notification.PRIORITY_DEFAULT)
+ .setDefaults(highPriority ? Notification.DEFAULT_ALL : 0)
+ .setOnlyAlertOnce(true);
+
+ if (notifyType == NotificationType.NETWORK_SWITCH) {
+ builder.setStyle(new Notification.BigTextStyle().bigText(details));
+ } else {
+ builder.setContentText(details);
+ }
+
+ Notification notification = builder.build();
+
+ try {
+ mNotificationManager.notifyAsUser(NOTIFICATION_ID, id, notification, UserHandle.ALL);
+ } catch (NullPointerException npe) {
+ Slog.d(TAG, "setNotificationVisible: visible notificationManager npe=" + npe);
+ }
+ }
+
+ public void clearNotification(int id) {
+ if (DBG) {
+ Slog.d(TAG, "clearNotification id=" + id);
+ }
+ try {
+ mNotificationManager.cancelAsUser(NOTIFICATION_ID, id, UserHandle.ALL);
+ } catch (NullPointerException npe) {
+ Slog.d(TAG, "setNotificationVisible: cancel notificationManager npe=" + npe);
+ }
+ }
+
+ /**
+ * Legacy provisioning notifications coming directly from DcTracker.
+ */
+ public void setProvNotificationVisible(boolean visible, int id, String action) {
+ if (visible) {
+ Intent intent = new Intent(action);
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ showNotification(id, NotificationType.SIGN_IN, null, null, pendingIntent, false);
+ } else {
+ clearNotification(id);
+ }
+ }
+
+ public void showToast(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) {
+ String fromTransport = getTransportName(getFirstTransportType(fromNai));
+ String toTransport = getTransportName(getFirstTransportType(toNai));
+ String text = mContext.getResources().getString(
+ R.string.network_switch_metered_toast, fromTransport, toTransport);
+ Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 22cefd1..7cd1b7b 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.CHANGE_NETWORK_STATE;
import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
+import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
@@ -65,10 +66,10 @@
private final BroadcastReceiver mIntentReceiver;
// Values are User IDs.
- private final Set<Integer> mUsers = new HashSet<Integer>();
+ private final Set<Integer> mUsers = new HashSet<>();
// Keys are App IDs. Values are true for SYSTEM permission and false for NETWORK permission.
- private final Map<Integer, Boolean> mApps = new HashMap<Integer, Boolean>();
+ private final Map<Integer, Boolean> mApps = new HashMap<>();
public PermissionMonitor(Context context, INetworkManagementService netd) {
mContext = context;
@@ -126,14 +127,14 @@
}
boolean isNetwork = hasNetworkPermission(app);
- boolean isSystem = hasSystemPermission(app);
+ boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
- if (isNetwork || isSystem) {
+ if (isNetwork || hasRestrictedPermission) {
Boolean permission = mApps.get(uid);
// If multiple packages share a UID (cf: android:sharedUserId) and ask for different
// permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
if (permission == null || permission == NETWORK) {
- mApps.put(uid, isSystem);
+ mApps.put(uid, hasRestrictedPermission);
}
}
}
@@ -164,12 +165,13 @@
return hasPermission(app, CHANGE_NETWORK_STATE);
}
- private boolean hasSystemPermission(PackageInfo app) {
+ private boolean hasRestrictedNetworkPermission(PackageInfo app) {
int flags = app.applicationInfo != null ? app.applicationInfo.flags : 0;
if ((flags & FLAG_SYSTEM) != 0 || (flags & FLAG_UPDATED_SYSTEM_APP) != 0) {
return true;
}
- return hasPermission(app, CONNECTIVITY_INTERNAL);
+ return hasPermission(app, CONNECTIVITY_INTERNAL)
+ || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
}
private int[] toIntArray(List<Integer> list) {
@@ -181,8 +183,8 @@
}
private void update(Set<Integer> users, Map<Integer, Boolean> apps, boolean add) {
- List<Integer> network = new ArrayList<Integer>();
- List<Integer> system = new ArrayList<Integer>();
+ List<Integer> network = new ArrayList<>();
+ List<Integer> system = new ArrayList<>();
for (Entry<Integer, Boolean> app : apps.entrySet()) {
List<Integer> list = app.getValue() ? system : network;
for (int user : users) {
@@ -209,7 +211,7 @@
}
mUsers.add(user);
- Set<Integer> users = new HashSet<Integer>();
+ Set<Integer> users = new HashSet<>();
users.add(user);
update(users, mApps, true);
}
@@ -221,7 +223,7 @@
}
mUsers.remove(user);
- Set<Integer> users = new HashSet<Integer>();
+ Set<Integer> users = new HashSet<>();
users.add(user);
update(users, mApps, false);
}
@@ -235,16 +237,16 @@
try {
PackageInfo app = mPackageManager.getPackageInfo(appName, GET_PERMISSIONS);
boolean isNetwork = hasNetworkPermission(app);
- boolean isSystem = hasSystemPermission(app);
- if (isNetwork || isSystem) {
+ boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
+ if (isNetwork || hasRestrictedPermission) {
Boolean permission = mApps.get(appUid);
// If multiple packages share a UID (cf: android:sharedUserId) and ask for different
// permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
if (permission == null || permission == NETWORK) {
- mApps.put(appUid, isSystem);
+ mApps.put(appUid, hasRestrictedPermission);
- Map<Integer, Boolean> apps = new HashMap<Integer, Boolean>();
- apps.put(appUid, isSystem);
+ Map<Integer, Boolean> apps = new HashMap<>();
+ apps.put(appUid, hasRestrictedPermission);
update(mUsers, apps, true);
}
}
@@ -260,7 +262,7 @@
}
mApps.remove(appUid);
- Map<Integer, Boolean> apps = new HashMap<Integer, Boolean>();
+ Map<Integer, Boolean> apps = new HashMap<>();
apps.put(appUid, NETWORK); // doesn't matter which permission we pick here
update(mUsers, apps, false);
}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 1012f9a..927f8f9 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -34,8 +34,6 @@
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.INetworkStatsService;
-import android.net.InterfaceConfiguration;
-import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
@@ -58,18 +56,21 @@
import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.util.IState;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.IoThread;
+import com.android.server.connectivity.tethering.IControlsTethering;
+import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
+import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
import com.android.server.net.BaseNetworkObserver;
import java.io.FileDescriptor;
@@ -81,18 +82,16 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @hide
*
- * Timeout
- *
- * TODO - look for parent classes and code sharing
+ * This class holds much of the business logic to allow Android devices
+ * to act as IP gateways via USB, BT, and WiFi interfaces.
*/
-public class Tethering extends BaseNetworkObserver {
+public class Tethering extends BaseNetworkObserver implements IControlsTethering {
private final Context mContext;
private final static String TAG = "Tethering";
@@ -100,7 +99,7 @@
private final static boolean VDBG = false;
private static final Class[] messageClasses = {
- Tethering.class, TetherMasterSM.class, TetherInterfaceSM.class
+ Tethering.class, TetherMasterSM.class, TetherInterfaceStateMachine.class
};
private static final SparseArray<String> sMagicDecoderRing =
MessageUtils.findMessageNames(messageClasses);
@@ -126,17 +125,25 @@
private final INetworkStatsService mStatsService;
private final Looper mLooper;
- private HashMap<String, TetherInterfaceSM> mIfaces; // all tethered/tetherable ifaces
+ private static class TetherState {
+ public final TetherInterfaceStateMachine mStateMachine;
+ public int mLastState;
+ public int mLastError;
+ public TetherState(TetherInterfaceStateMachine sm) {
+ mStateMachine = sm;
+ // Assume all state machines start out available and with no errors.
+ mLastState = IControlsTethering.STATE_AVAILABLE;
+ mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
+ }
+ }
+ private final ArrayMap<String, TetherState> mTetherStates;
- private BroadcastReceiver mStateReceiver;
+ private final BroadcastReceiver mStateReceiver;
// {@link ComponentName} of the Service used to run tether provisioning.
private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(Resources
.getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
- private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129";
- private static final int USB_PREFIX_LENGTH = 24;
-
// USB is 192.168.42.1 and 255.255.255.0
// Wifi is 192.168.43.1 and 255.255.255.0
// BT is limited to max default of 5 connections. 192.168.44.1 to 192.168.48.1
@@ -166,6 +173,9 @@
private boolean mUsbTetherRequested; // true if USB tethering should be started
// when RNDIS is enabled
+ // True iff WiFi tethering should be started when soft AP is ready.
+ private boolean mWifiTetherRequested;
+
public Tethering(Context context, INetworkManagementService nmService,
INetworkStatsService statsService) {
mContext = context;
@@ -174,7 +184,7 @@
mPublicSync = new Object();
- mIfaces = new HashMap<String, TetherInterfaceSM>();
+ mTetherStates = new ArrayMap<>();
// make our own thread so we don't anr the system
mLooper = IoThread.get().getLooper();
@@ -187,6 +197,7 @@
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_STATE);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+ filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
mContext.registerReceiver(mStateReceiver, filter);
@@ -227,7 +238,7 @@
int ifaceTypes[] = mContext.getResources().getIntArray(
com.android.internal.R.array.config_tether_upstream_types);
- Collection<Integer> upstreamIfaceTypes = new ArrayList();
+ Collection<Integer> upstreamIfaceTypes = new ArrayList<>();
for (int i : ifaceTypes) {
upstreamIfaceTypes.add(new Integer(i));
}
@@ -248,34 +259,28 @@
// Never called directly: only called from interfaceLinkStateChanged.
// See NetlinkHandler.cpp:71.
if (VDBG) Log.d(TAG, "interfaceStatusChanged " + iface + ", " + up);
- boolean found = false;
- boolean usb = false;
synchronized (mPublicSync) {
- if (isWifi(iface)) {
- found = true;
- } else if (isUsb(iface)) {
- found = true;
- usb = true;
- } else if (isBluetooth(iface)) {
- found = true;
+ int interfaceType = ifaceNameToType(iface);
+ if (interfaceType == ConnectivityManager.TETHERING_INVALID) {
+ return;
}
- if (found == false) return;
- TetherInterfaceSM sm = mIfaces.get(iface);
+ TetherState tetherState = mTetherStates.get(iface);
if (up) {
- if (sm == null) {
- sm = new TetherInterfaceSM(iface, mLooper, usb);
- mIfaces.put(iface, sm);
- sm.start();
+ if (tetherState == null) {
+ trackNewTetherableInterface(iface, interfaceType);
}
} else {
- if (isUsb(iface)) {
- // ignore usb0 down after enabling RNDIS
- // we will handle disconnect in interfaceRemoved instead
+ if (interfaceType == ConnectivityManager.TETHERING_BLUETOOTH) {
+ tetherState.mStateMachine.sendMessage(
+ TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
+ mTetherStates.remove(iface);
+ } else {
+ // Ignore usb0 down after enabling RNDIS.
+ // We will handle disconnect in interfaceRemoved.
+ // Similarly, ignore interface down for WiFi. We monitor WiFi AP status
+ // through the WifiManager.WIFI_AP_STATE_CHANGED_ACTION intent.
if (VDBG) Log.d(TAG, "ignore interface down for " + iface);
- } else if (sm != null) {
- sm.sendMessage(TetherInterfaceSM.CMD_INTERFACE_DOWN);
- mIfaces.remove(iface);
}
}
}
@@ -295,7 +300,7 @@
}
}
- public boolean isWifi(String iface) {
+ private boolean isWifi(String iface) {
synchronized (mPublicSync) {
for (String regex : mTetherableWifiRegexs) {
if (iface.matches(regex)) return true;
@@ -304,7 +309,7 @@
}
}
- public boolean isBluetooth(String iface) {
+ private boolean isBluetooth(String iface) {
synchronized (mPublicSync) {
for (String regex : mTetherableBluetoothRegexs) {
if (iface.matches(regex)) return true;
@@ -313,35 +318,33 @@
}
}
+ private int ifaceNameToType(String iface) {
+ if (isWifi(iface)) {
+ return ConnectivityManager.TETHERING_WIFI;
+ } else if (isUsb(iface)) {
+ return ConnectivityManager.TETHERING_USB;
+ } else if (isBluetooth(iface)) {
+ return ConnectivityManager.TETHERING_BLUETOOTH;
+ }
+ return ConnectivityManager.TETHERING_INVALID;
+ }
+
@Override
public void interfaceAdded(String iface) {
if (VDBG) Log.d(TAG, "interfaceAdded " + iface);
- boolean found = false;
- boolean usb = false;
synchronized (mPublicSync) {
- if (isWifi(iface)) {
- found = true;
- }
- if (isUsb(iface)) {
- found = true;
- usb = true;
- }
- if (isBluetooth(iface)) {
- found = true;
- }
- if (found == false) {
+ int interfaceType = ifaceNameToType(iface);
+ if (interfaceType == ConnectivityManager.TETHERING_INVALID) {
if (VDBG) Log.d(TAG, iface + " is not a tetherable iface, ignoring");
return;
}
- TetherInterfaceSM sm = mIfaces.get(iface);
- if (sm != null) {
+ TetherState tetherState = mTetherStates.get(iface);
+ if (tetherState == null) {
+ trackNewTetherableInterface(iface, interfaceType);
+ } else {
if (VDBG) Log.d(TAG, "active iface (" + iface + ") reported as added, ignoring");
- return;
}
- sm = new TetherInterfaceSM(iface, mLooper, usb);
- mIfaces.put(iface, sm);
- sm.start();
}
}
@@ -349,15 +352,15 @@
public void interfaceRemoved(String iface) {
if (VDBG) Log.d(TAG, "interfaceRemoved " + iface);
synchronized (mPublicSync) {
- TetherInterfaceSM sm = mIfaces.get(iface);
- if (sm == null) {
+ TetherState tetherState = mTetherStates.get(iface);
+ if (tetherState == null) {
if (VDBG) {
Log.e(TAG, "attempting to remove unknown iface (" + iface + "), ignoring");
}
return;
}
- sm.sendMessage(TetherInterfaceSM.CMD_INTERFACE_DOWN);
- mIfaces.remove(iface);
+ tetherState.mStateMachine.sendMessage(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
+ mTetherStates.remove(iface);
}
}
@@ -413,24 +416,19 @@
* for the specified interface.
*/
private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) {
- boolean isProvisioningRequired = isTetherProvisioningRequired();
+ boolean isProvisioningRequired = enable && isTetherProvisioningRequired();
+ int result;
switch (type) {
case ConnectivityManager.TETHERING_WIFI:
- final WifiManager wifiManager =
- (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
- if (wifiManager.setWifiApEnabled(null, enable)) {
- sendTetherResult(receiver, ConnectivityManager.TETHER_ERROR_NO_ERROR);
- if (enable && isProvisioningRequired) {
- scheduleProvisioningRechecks(type);
- }
- } else{
- sendTetherResult(receiver, ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
+ result = setWifiTethering(enable);
+ if (isProvisioningRequired && result == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ scheduleProvisioningRechecks(type);
}
+ sendTetherResult(receiver, result);
break;
case ConnectivityManager.TETHERING_USB:
- int result = setUsbTethering(enable);
- if (enable && isProvisioningRequired &&
- result == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ result = setUsbTethering(enable);
+ if (isProvisioningRequired && result == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
scheduleProvisioningRechecks(type);
}
sendTetherResult(receiver, result);
@@ -450,6 +448,18 @@
}
}
+ private int setWifiTethering(final boolean enable) {
+ synchronized (mPublicSync) {
+ mWifiTetherRequested = enable;
+ final WifiManager wifiManager =
+ (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ if (wifiManager.setWifiApEnabled(null /* use existing wifi config */, enable)) {
+ return ConnectivityManager.TETHER_ERROR_NO_ERROR;
+ }
+ return ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
+ }
+ }
+
private void setBluetoothTethering(final boolean enable, final ResultReceiver receiver) {
final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter == null || !adapter.isEnabled()) {
@@ -576,62 +586,62 @@
public int tether(String iface) {
if (DBG) Log.d(TAG, "Tethering " + iface);
- TetherInterfaceSM sm = null;
synchronized (mPublicSync) {
- sm = mIfaces.get(iface);
+ TetherState tetherState = mTetherStates.get(iface);
+ if (tetherState == null) {
+ Log.e(TAG, "Tried to Tether an unknown iface: " + iface + ", ignoring");
+ return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
+ }
+ // Ignore the error status of the interface. If the interface is available,
+ // the errors are referring to past tethering attempts anyway.
+ if (tetherState.mLastState != IControlsTethering.STATE_AVAILABLE) {
+ Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring");
+ return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
+ }
+ tetherState.mStateMachine.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED);
+ return ConnectivityManager.TETHER_ERROR_NO_ERROR;
}
- if (sm == null) {
- Log.e(TAG, "Tried to Tether an unknown iface :" + iface + ", ignoring");
- return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
- }
- if (!sm.isAvailable() && !sm.isErrored()) {
- Log.e(TAG, "Tried to Tether an unavailable iface :" + iface + ", ignoring");
- return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
- }
- sm.sendMessage(TetherInterfaceSM.CMD_TETHER_REQUESTED);
- return ConnectivityManager.TETHER_ERROR_NO_ERROR;
}
public int untether(String iface) {
if (DBG) Log.d(TAG, "Untethering " + iface);
- TetherInterfaceSM sm = null;
synchronized (mPublicSync) {
- sm = mIfaces.get(iface);
+ TetherState tetherState = mTetherStates.get(iface);
+ if (tetherState == null) {
+ Log.e(TAG, "Tried to Untether an unknown iface :" + iface + ", ignoring");
+ return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
+ }
+ if (tetherState.mLastState != IControlsTethering.STATE_TETHERED) {
+ Log.e(TAG, "Tried to untether an untethered iface :" + iface + ", ignoring");
+ return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
+ }
+ tetherState.mStateMachine.sendMessage(
+ TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
+ return ConnectivityManager.TETHER_ERROR_NO_ERROR;
}
- if (sm == null) {
- Log.e(TAG, "Tried to Untether an unknown iface :" + iface + ", ignoring");
- return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
- }
- if (sm.isErrored()) {
- Log.e(TAG, "Tried to Untethered an errored iface :" + iface + ", ignoring");
- return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
- }
- sm.sendMessage(TetherInterfaceSM.CMD_TETHER_UNREQUESTED);
- return ConnectivityManager.TETHER_ERROR_NO_ERROR;
}
public void untetherAll() {
- if (DBG) Log.d(TAG, "Untethering " + mIfaces);
- for (String iface : mIfaces.keySet()) {
- untether(iface);
+ synchronized (mPublicSync) {
+ if (DBG) Log.d(TAG, "Untethering " + mTetherStates.keySet());
+ for (int i = 0; i < mTetherStates.size(); i++) {
+ untether(mTetherStates.keyAt(i));
+ }
}
}
public int getLastTetherError(String iface) {
- TetherInterfaceSM sm = null;
synchronized (mPublicSync) {
- sm = mIfaces.get(iface);
- if (sm == null) {
+ TetherState tetherState = mTetherStates.get(iface);
+ if (tetherState == null) {
Log.e(TAG, "Tried to getLastTetherError on an unknown iface :" + iface +
", ignoring");
return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
}
- return sm.getLastError();
+ return tetherState.mLastError;
}
}
- // TODO - move all private methods used only by the state machine into the state machine
- // to clarify what needs synchronized protection.
private void sendTetherStateChangedBroadcast() {
if (!getConnectivityManager().isTetheringSupported()) return;
@@ -644,24 +654,22 @@
boolean bluetoothTethered = false;
synchronized (mPublicSync) {
- Set ifaces = mIfaces.keySet();
- for (Object iface : ifaces) {
- TetherInterfaceSM sm = mIfaces.get(iface);
- if (sm != null) {
- if (sm.isErrored()) {
- erroredList.add((String)iface);
- } else if (sm.isAvailable()) {
- availableList.add((String)iface);
- } else if (sm.isTethered()) {
- if (isUsb((String)iface)) {
- usbTethered = true;
- } else if (isWifi((String)iface)) {
- wifiTethered = true;
- } else if (isBluetooth((String)iface)) {
- bluetoothTethered = true;
- }
- activeList.add((String)iface);
+ for (int i = 0; i < mTetherStates.size(); i++) {
+ TetherState tetherState = mTetherStates.valueAt(i);
+ String iface = mTetherStates.keyAt(i);
+ if (tetherState.mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ erroredList.add(iface);
+ } else if (tetherState.mLastState == IControlsTethering.STATE_AVAILABLE) {
+ availableList.add(iface);
+ } else if (tetherState.mLastState == IControlsTethering.STATE_TETHERED) {
+ if (isUsb(iface)) {
+ usbTethered = true;
+ } else if (isWifi(iface)) {
+ wifiTethered = true;
+ } else if (isBluetooth(iface)) {
+ bluetoothTethered = true;
}
+ activeList.add(iface);
}
}
}
@@ -770,7 +778,7 @@
mRndisEnabled = intent.getBooleanExtra(UsbManager.USB_FUNCTION_RNDIS, false);
// start tethering if we have a request pending
if (usbConnected && mRndisEnabled && mUsbTetherRequested) {
- tetherUsb(true);
+ tetherMatchingInterfaces(true, ConnectivityManager.TETHERING_USB);
}
mUsbTetherRequested = false;
}
@@ -782,69 +790,80 @@
if (VDBG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION");
mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
}
+ } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
+ synchronized (Tethering.this.mPublicSync) {
+ int curState = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE,
+ WifiManager.WIFI_AP_STATE_DISABLED);
+ switch (curState) {
+ case WifiManager.WIFI_AP_STATE_ENABLING:
+ // We can see this state on the way to both enabled and failure states.
+ break;
+ case WifiManager.WIFI_AP_STATE_ENABLED:
+ // When the AP comes up and we've been requested to tether it, do so.
+ if (mWifiTetherRequested) {
+ tetherMatchingInterfaces(true, ConnectivityManager.TETHERING_WIFI);
+ }
+ break;
+ case WifiManager.WIFI_AP_STATE_DISABLED:
+ case WifiManager.WIFI_AP_STATE_DISABLING:
+ case WifiManager.WIFI_AP_STATE_FAILED:
+ default:
+ if (DBG) {
+ Log.d(TAG, "Canceling WiFi tethering request - AP_STATE=" +
+ curState);
+ }
+ // Tell appropriate interface state machines that they should tear
+ // themselves down.
+ for (int i = 0; i < mTetherStates.size(); i++) {
+ TetherInterfaceStateMachine tism =
+ mTetherStates.valueAt(i).mStateMachine;
+ if (tism.interfaceType() == ConnectivityManager.TETHERING_WIFI) {
+ tism.sendMessage(
+ TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
+ break; // There should be at most one of these.
+ }
+ }
+ // Regardless of whether we requested this transition, the AP has gone
+ // down. Don't try to tether again unless we're requested to do so.
+ mWifiTetherRequested = false;
+ break;
+ }
+ }
} else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
updateConfiguration();
}
}
}
- private void tetherUsb(boolean enable) {
- if (VDBG) Log.d(TAG, "tetherUsb " + enable);
+ private void tetherMatchingInterfaces(boolean enable, int interfaceType) {
+ if (VDBG) Log.d(TAG, "tetherMatchingInterfaces(" + enable + ", " + interfaceType + ")");
- String[] ifaces = new String[0];
+ String[] ifaces = null;
try {
ifaces = mNMService.listInterfaces();
} catch (Exception e) {
Log.e(TAG, "Error listing Interfaces", e);
return;
}
- for (String iface : ifaces) {
- if (isUsb(iface)) {
- int result = (enable ? tether(iface) : untether(iface));
- if (result == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
- return;
+ String chosenIface = null;
+ if (ifaces != null) {
+ for (String iface : ifaces) {
+ if (ifaceNameToType(iface) == interfaceType) {
+ chosenIface = iface;
+ break;
}
}
}
- Log.e(TAG, "unable start or stop USB tethering");
- }
-
- // configured when we start tethering and unconfig'd on error or conclusion
- private boolean configureUsbIface(boolean enabled) {
- if (VDBG) Log.d(TAG, "configureUsbIface(" + enabled + ")");
-
- // toggle the USB interfaces
- String[] ifaces = new String[0];
- try {
- ifaces = mNMService.listInterfaces();
- } catch (Exception e) {
- Log.e(TAG, "Error listing Interfaces", e);
- return false;
+ if (chosenIface == null) {
+ Log.e(TAG, "could not find iface of type " + interfaceType);
+ return;
}
- for (String iface : ifaces) {
- if (isUsb(iface)) {
- InterfaceConfiguration ifcg = null;
- try {
- ifcg = mNMService.getInterfaceConfig(iface);
- if (ifcg != null) {
- InetAddress addr = NetworkUtils.numericToInetAddress(USB_NEAR_IFACE_ADDR);
- ifcg.setLinkAddress(new LinkAddress(addr, USB_PREFIX_LENGTH));
- if (enabled) {
- ifcg.setInterfaceUp();
- } else {
- ifcg.setInterfaceDown();
- }
- ifcg.clearFlag("running");
- mNMService.setInterfaceConfig(iface, ifcg);
- }
- } catch (Exception e) {
- Log.e(TAG, "Error configuring interface " + iface, e);
- return false;
- }
- }
- }
- return true;
+ int result = (enable ? tether(chosenIface) : untether(chosenIface));
+ if (result != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ Log.e(TAG, "unable start or stop tethering on iface " + chosenIface);
+ return;
+ }
}
// TODO - return copies so people can't tamper
@@ -869,7 +888,7 @@
if (mRndisEnabled) {
final long ident = Binder.clearCallingIdentity();
try {
- tetherUsb(true);
+ tetherMatchingInterfaces(true, ConnectivityManager.TETHERING_USB);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -880,7 +899,7 @@
} else {
final long ident = Binder.clearCallingIdentity();
try {
- tetherUsb(false);
+ tetherMatchingInterfaces(false, ConnectivityManager.TETHERING_USB);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -952,37 +971,27 @@
public String[] getTetheredIfaces() {
ArrayList<String> list = new ArrayList<String>();
synchronized (mPublicSync) {
- Set keys = mIfaces.keySet();
- for (Object key : keys) {
- TetherInterfaceSM sm = mIfaces.get(key);
- if (sm.isTethered()) {
- list.add((String)key);
+ for (int i = 0; i < mTetherStates.size(); i++) {
+ TetherState tetherState = mTetherStates.valueAt(i);
+ if (tetherState.mLastState == IControlsTethering.STATE_TETHERED) {
+ list.add(mTetherStates.keyAt(i));
}
}
}
- String[] retVal = new String[list.size()];
- for (int i=0; i < list.size(); i++) {
- retVal[i] = list.get(i);
- }
- return retVal;
+ return list.toArray(new String[list.size()]);
}
public String[] getTetherableIfaces() {
ArrayList<String> list = new ArrayList<String>();
synchronized (mPublicSync) {
- Set keys = mIfaces.keySet();
- for (Object key : keys) {
- TetherInterfaceSM sm = mIfaces.get(key);
- if (sm.isAvailable()) {
- list.add((String)key);
+ for (int i = 0; i < mTetherStates.size(); i++) {
+ TetherState tetherState = mTetherStates.valueAt(i);
+ if (tetherState.mLastState == IControlsTethering.STATE_AVAILABLE) {
+ list.add(mTetherStates.keyAt(i));
}
}
}
- String[] retVal = new String[list.size()];
- for (int i=0; i < list.size(); i++) {
- retVal[i] = list.get(i);
- }
- return retVal;
+ return list.toArray(new String[list.size()]);
}
public String[] getTetheredDhcpRanges() {
@@ -992,19 +1001,14 @@
public String[] getErroredIfaces() {
ArrayList<String> list = new ArrayList<String>();
synchronized (mPublicSync) {
- Set keys = mIfaces.keySet();
- for (Object key : keys) {
- TetherInterfaceSM sm = mIfaces.get(key);
- if (sm.isErrored()) {
- list.add((String)key);
+ for (int i = 0; i < mTetherStates.size(); i++) {
+ TetherState tetherState = mTetherStates.valueAt(i);
+ if (tetherState.mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ list.add(mTetherStates.keyAt(i));
}
}
}
- String[] retVal = new String[list.size()];
- for (int i= 0; i< list.size(); i++) {
- retVal[i] = list.get(i);
- }
- return retVal;
+ return list.toArray(new String[list.size()]);
}
private void maybeLogMessage(State state, int what) {
@@ -1014,416 +1018,35 @@
}
}
- class TetherInterfaceSM extends StateMachine {
- private static final int BASE_IFACE = Protocol.BASE_TETHERING + 100;
- // notification from the master SM that it's not in tether mode
- static final int CMD_TETHER_MODE_DEAD = BASE_IFACE + 1;
- // request from the user that it wants to tether
- static final int CMD_TETHER_REQUESTED = BASE_IFACE + 2;
- // request from the user that it wants to untether
- static final int CMD_TETHER_UNREQUESTED = BASE_IFACE + 3;
- // notification that this interface is down
- static final int CMD_INTERFACE_DOWN = BASE_IFACE + 4;
- // notification that this interface is up
- static final int CMD_INTERFACE_UP = BASE_IFACE + 5;
- // notification from the master SM that it had an error turning on cellular dun
- static final int CMD_CELL_DUN_ERROR = BASE_IFACE + 6;
- // notification from the master SM that it had trouble enabling IP Forwarding
- static final int CMD_IP_FORWARDING_ENABLE_ERROR = BASE_IFACE + 7;
- // notification from the master SM that it had trouble disabling IP Forwarding
- static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IFACE + 8;
- // notification from the master SM that it had trouble starting tethering
- static final int CMD_START_TETHERING_ERROR = BASE_IFACE + 9;
- // notification from the master SM that it had trouble stopping tethering
- static final int CMD_STOP_TETHERING_ERROR = BASE_IFACE + 10;
- // notification from the master SM that it had trouble setting the DNS forwarders
- static final int CMD_SET_DNS_FORWARDERS_ERROR = BASE_IFACE + 11;
- // the upstream connection has changed
- static final int CMD_TETHER_CONNECTION_CHANGED = BASE_IFACE + 12;
-
- private State mDefaultState;
-
- private State mInitialState;
- private State mStartingState;
- private State mTetheredState;
-
- private State mUnavailableState;
-
- private boolean mAvailable;
- private boolean mTethered;
- int mLastError;
-
- String mIfaceName;
- String mMyUpstreamIfaceName; // may change over time
-
- boolean mUsb;
-
- TetherInterfaceSM(String name, Looper looper, boolean usb) {
- super(name, looper);
- mIfaceName = name;
- mUsb = usb;
- setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
-
- mInitialState = new InitialState();
- addState(mInitialState);
- mStartingState = new StartingState();
- addState(mStartingState);
- mTetheredState = new TetheredState();
- addState(mTetheredState);
- mUnavailableState = new UnavailableState();
- addState(mUnavailableState);
-
- setInitialState(mInitialState);
- }
-
- public String toString() {
- String res = new String();
- res += mIfaceName + " - ";
- IState current = getCurrentState();
- if (current == mInitialState) res += "InitialState";
- if (current == mStartingState) res += "StartingState";
- if (current == mTetheredState) res += "TetheredState";
- if (current == mUnavailableState) res += "UnavailableState";
- if (mAvailable) res += " - Available";
- if (mTethered) res += " - Tethered";
- res += " - lastError =" + mLastError;
- return res;
- }
-
- public int getLastError() {
- synchronized (Tethering.this.mPublicSync) {
- return mLastError;
- }
- }
-
- private void setLastError(int error) {
- synchronized (Tethering.this.mPublicSync) {
- mLastError = error;
-
- if (isErrored()) {
- if (mUsb) {
- // note everything's been unwound by this point so nothing to do on
- // further error..
- Tethering.this.configureUsbIface(false);
- }
- }
- }
- }
-
- public boolean isAvailable() {
- synchronized (Tethering.this.mPublicSync) {
- return mAvailable;
- }
- }
-
- private void setAvailable(boolean available) {
- synchronized (Tethering.this.mPublicSync) {
- mAvailable = available;
- }
- }
-
- public boolean isTethered() {
- synchronized (Tethering.this.mPublicSync) {
- return mTethered;
- }
- }
-
- private void setTethered(boolean tethered) {
- synchronized (Tethering.this.mPublicSync) {
- mTethered = tethered;
- }
- }
-
- public boolean isErrored() {
- synchronized (Tethering.this.mPublicSync) {
- return (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR);
- }
- }
-
- class InitialState extends State {
- @Override
- public void enter() {
- setAvailable(true);
- setTethered(false);
- sendTetherStateChangedBroadcast();
- }
-
- @Override
- public boolean processMessage(Message message) {
- maybeLogMessage(this, message.what);
- boolean retValue = true;
- switch (message.what) {
- case CMD_TETHER_REQUESTED:
- setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
- mTetherMasterSM.sendMessage(TetherMasterSM.CMD_TETHER_MODE_REQUESTED,
- TetherInterfaceSM.this);
- transitionTo(mStartingState);
- break;
- case CMD_INTERFACE_DOWN:
- transitionTo(mUnavailableState);
- break;
- default:
- retValue = false;
- break;
- }
- return retValue;
- }
- }
-
- class StartingState extends State {
- @Override
- public void enter() {
- setAvailable(false);
- if (mUsb) {
- if (!Tethering.this.configureUsbIface(true)) {
- mTetherMasterSM.sendMessage(TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED,
- TetherInterfaceSM.this);
- setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
-
- transitionTo(mInitialState);
- return;
- }
- }
- sendTetherStateChangedBroadcast();
-
- // Skipping StartingState
- transitionTo(mTetheredState);
- }
- @Override
- public boolean processMessage(Message message) {
- maybeLogMessage(this, message.what);
- boolean retValue = true;
- switch (message.what) {
- // maybe a parent class?
- case CMD_TETHER_UNREQUESTED:
- mTetherMasterSM.sendMessage(TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED,
- TetherInterfaceSM.this);
- if (mUsb) {
- if (!Tethering.this.configureUsbIface(false)) {
- setLastErrorAndTransitionToInitialState(
- ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
- break;
- }
- }
- transitionTo(mInitialState);
- break;
- case CMD_CELL_DUN_ERROR:
- case CMD_IP_FORWARDING_ENABLE_ERROR:
- case CMD_IP_FORWARDING_DISABLE_ERROR:
- case CMD_START_TETHERING_ERROR:
- case CMD_STOP_TETHERING_ERROR:
- case CMD_SET_DNS_FORWARDERS_ERROR:
- setLastErrorAndTransitionToInitialState(
- ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
- break;
- case CMD_INTERFACE_DOWN:
- mTetherMasterSM.sendMessage(TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED,
- TetherInterfaceSM.this);
- transitionTo(mUnavailableState);
- break;
- default:
- retValue = false;
- }
- return retValue;
- }
- }
-
- class TetheredState extends State {
- @Override
- public void enter() {
- try {
- mNMService.tetherInterface(mIfaceName);
- } catch (Exception e) {
- Log.e(TAG, "Error Tethering: " + e.toString());
- setLastError(ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR);
-
- try {
- mNMService.untetherInterface(mIfaceName);
- } catch (Exception ee) {
- Log.e(TAG, "Error untethering after failure!" + ee.toString());
- }
- transitionTo(mInitialState);
- return;
- }
- if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
- setAvailable(false);
- setTethered(true);
- sendTetherStateChangedBroadcast();
- }
-
- private void cleanupUpstream() {
- if (mMyUpstreamIfaceName != null) {
- // note that we don't care about errors here.
- // sometimes interfaces are gone before we get
- // to remove their rules, which generates errors.
- // just do the best we can.
- try {
- // about to tear down NAT; gather remaining statistics
- mStatsService.forceUpdate();
- } catch (Exception e) {
- if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString());
- }
- try {
- mNMService.stopInterfaceForwarding(mIfaceName, mMyUpstreamIfaceName);
- } catch (Exception e) {
- if (VDBG) Log.e(
- TAG, "Exception in removeInterfaceForward: " + e.toString());
- }
- try {
- mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
- } catch (Exception e) {
- if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString());
- }
- mMyUpstreamIfaceName = null;
- }
- return;
- }
-
- @Override
- public boolean processMessage(Message message) {
- maybeLogMessage(this, message.what);
- boolean retValue = true;
- boolean error = false;
- switch (message.what) {
- case CMD_TETHER_UNREQUESTED:
- case CMD_INTERFACE_DOWN:
- cleanupUpstream();
- try {
- mNMService.untetherInterface(mIfaceName);
- } catch (Exception e) {
- setLastErrorAndTransitionToInitialState(
- ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
- break;
- }
- mTetherMasterSM.sendMessage(TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED,
- TetherInterfaceSM.this);
- if (message.what == CMD_TETHER_UNREQUESTED) {
- if (mUsb) {
- if (!Tethering.this.configureUsbIface(false)) {
- setLastError(
- ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
- }
- }
- transitionTo(mInitialState);
- } else if (message.what == CMD_INTERFACE_DOWN) {
- transitionTo(mUnavailableState);
- }
- if (DBG) Log.d(TAG, "Untethered " + mIfaceName);
- break;
- case CMD_TETHER_CONNECTION_CHANGED:
- String newUpstreamIfaceName = (String)(message.obj);
- if ((mMyUpstreamIfaceName == null && newUpstreamIfaceName == null) ||
- (mMyUpstreamIfaceName != null &&
- mMyUpstreamIfaceName.equals(newUpstreamIfaceName))) {
- if (VDBG) Log.d(TAG, "Connection changed noop - dropping");
- break;
- }
- cleanupUpstream();
- if (newUpstreamIfaceName != null) {
- try {
- mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
- mNMService.startInterfaceForwarding(mIfaceName,
- newUpstreamIfaceName);
- } catch (Exception e) {
- Log.e(TAG, "Exception enabling Nat: " + e.toString());
- try {
- mNMService.disableNat(mIfaceName, newUpstreamIfaceName);
- } catch (Exception ee) {}
- try {
- mNMService.untetherInterface(mIfaceName);
- } catch (Exception ee) {}
-
- setLastError(ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR);
- transitionTo(mInitialState);
- return true;
- }
- }
- mMyUpstreamIfaceName = newUpstreamIfaceName;
- break;
- case CMD_CELL_DUN_ERROR:
- case CMD_IP_FORWARDING_ENABLE_ERROR:
- case CMD_IP_FORWARDING_DISABLE_ERROR:
- case CMD_START_TETHERING_ERROR:
- case CMD_STOP_TETHERING_ERROR:
- case CMD_SET_DNS_FORWARDERS_ERROR:
- error = true;
- // fall through
- case CMD_TETHER_MODE_DEAD:
- cleanupUpstream();
- try {
- mNMService.untetherInterface(mIfaceName);
- } catch (Exception e) {
- setLastErrorAndTransitionToInitialState(
- ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
- break;
- }
- if (error) {
- setLastErrorAndTransitionToInitialState(
- ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
- break;
- }
- if (DBG) Log.d(TAG, "Tether lost upstream connection " + mIfaceName);
- sendTetherStateChangedBroadcast();
- if (mUsb) {
- if (!Tethering.this.configureUsbIface(false)) {
- setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
- }
- }
- transitionTo(mInitialState);
- break;
- default:
- retValue = false;
- break;
- }
- return retValue;
- }
- }
-
- class UnavailableState extends State {
- @Override
- public void enter() {
- setAvailable(false);
- setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
- setTethered(false);
- sendTetherStateChangedBroadcast();
- }
- @Override
- public boolean processMessage(Message message) {
- boolean retValue = true;
- switch (message.what) {
- case CMD_INTERFACE_UP:
- transitionTo(mInitialState);
- break;
- default:
- retValue = false;
- break;
- }
- return retValue;
- }
- }
-
- void setLastErrorAndTransitionToInitialState(int error) {
- setLastError(error);
- transitionTo(mInitialState);
- }
-
- }
-
/**
* A NetworkCallback class that relays information of interest to the
* tethering master state machine thread for subsequent processing.
*/
class UpstreamNetworkCallback extends NetworkCallback {
@Override
+ public void onAvailable(Network network) {
+ mTetherMasterSM.sendMessage(TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
+ UpstreamNetworkMonitor.EVENT_ON_AVAILABLE, 0, network);
+ }
+
+ @Override
+ public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
+ mTetherMasterSM.sendMessage(TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
+ UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES, 0,
+ new NetworkState(null, null, newNc, network, null, null));
+ }
+
+ @Override
public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
- mTetherMasterSM.sendMessage(
- TetherMasterSM.EVENT_UPSTREAM_LINKPROPERTIES_CHANGED,
+ mTetherMasterSM.sendMessage(TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
+ UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES, 0,
new NetworkState(null, newLp, null, network, null, null));
}
@Override
public void onLost(Network network) {
- mTetherMasterSM.sendMessage(TetherMasterSM.EVENT_UPSTREAM_LOST, network);
+ mTetherMasterSM.sendMessage(TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
+ UpstreamNetworkMonitor.EVENT_ON_LOST, 0, network);
}
}
@@ -1442,7 +1065,12 @@
* could/should be moved here.
*/
class UpstreamNetworkMonitor {
- final HashMap<Network, NetworkState> mNetworkMap = new HashMap();
+ static final int EVENT_ON_AVAILABLE = 1;
+ static final int EVENT_ON_CAPABILITIES = 2;
+ static final int EVENT_ON_LINKPROPERTIES = 3;
+ static final int EVENT_ON_LOST = 4;
+
+ final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
NetworkCallback mDefaultNetworkCallback;
NetworkCallback mDunTetheringCallback;
@@ -1476,33 +1104,107 @@
mNetworkMap.clear();
}
- // Returns true if these updated LinkProperties pertain to the current
- // upstream network interface, false otherwise (or if there is not
- // currently any upstream tethering interface).
- boolean processLinkPropertiesChanged(NetworkState networkState) {
- if (networkState == null ||
- networkState.network == null ||
- networkState.linkProperties == null) {
- return false;
- }
+ NetworkState lookup(Network network) {
+ return (network != null) ? mNetworkMap.get(network) : null;
+ }
- mNetworkMap.put(networkState.network, networkState);
-
- if (mCurrentUpstreamIface != null) {
- for (String ifname : networkState.linkProperties.getAllInterfaceNames()) {
- if (mCurrentUpstreamIface.equals(ifname)) {
- return true;
+ NetworkState processCallback(int arg1, Object obj) {
+ switch (arg1) {
+ case EVENT_ON_AVAILABLE: {
+ final Network network = (Network) obj;
+ if (VDBG) {
+ Log.d(TAG, "EVENT_ON_AVAILABLE for " + network);
}
+ if (!mNetworkMap.containsKey(network)) {
+ mNetworkMap.put(network,
+ new NetworkState(null, null, null, network, null, null));
+ }
+
+ final ConnectivityManager cm = getConnectivityManager();
+
+ if (mDefaultNetworkCallback != null) {
+ cm.requestNetworkCapabilities(mDefaultNetworkCallback);
+ cm.requestLinkProperties(mDefaultNetworkCallback);
+ }
+
+ // Requesting updates for mDunTetheringCallback is not
+ // necessary. Because it's a listen, it will already have
+ // heard all NetworkCapabilities and LinkProperties updates
+ // since UpstreamNetworkMonitor was started. Because we
+ // start UpstreamNetworkMonitor before chooseUpstreamType()
+ // is ever invoked (it can register a DUN request) this is
+ // mostly safe. However, if a DUN network is already up for
+ // some reason (unlikely, because DUN is restricted and,
+ // unless the DUN network is shared with another APN, only
+ // the system can request it and this is the only part of
+ // the system that requests it) we won't know its
+ // LinkProperties or NetworkCapabilities.
+
+ return mNetworkMap.get(network);
+ }
+ case EVENT_ON_CAPABILITIES: {
+ final NetworkState ns = (NetworkState) obj;
+ if (!mNetworkMap.containsKey(ns.network)) {
+ // Ignore updates for networks for which we have not yet
+ // received onAvailable() - which should never happen -
+ // or for which we have already received onLost().
+ return null;
+ }
+ if (VDBG) {
+ Log.d(TAG, String.format("EVENT_ON_CAPABILITIES for %s: %s",
+ ns.network, ns.networkCapabilities));
+ }
+
+ final NetworkState prev = mNetworkMap.get(ns.network);
+ mNetworkMap.put(ns.network,
+ new NetworkState(null, prev.linkProperties, ns.networkCapabilities,
+ ns.network, null, null));
+ return mNetworkMap.get(ns.network);
+ }
+ case EVENT_ON_LINKPROPERTIES: {
+ final NetworkState ns = (NetworkState) obj;
+ if (!mNetworkMap.containsKey(ns.network)) {
+ // Ignore updates for networks for which we have not yet
+ // received onAvailable() - which should never happen -
+ // or for which we have already received onLost().
+ return null;
+ }
+ if (VDBG) {
+ Log.d(TAG, String.format("EVENT_ON_LINKPROPERTIES for %s: %s",
+ ns.network, ns.linkProperties));
+ }
+
+ final NetworkState prev = mNetworkMap.get(ns.network);
+ mNetworkMap.put(ns.network,
+ new NetworkState(null, ns.linkProperties, prev.networkCapabilities,
+ ns.network, null, null));
+ return mNetworkMap.get(ns.network);
+ }
+ case EVENT_ON_LOST: {
+ final Network network = (Network) obj;
+ if (VDBG) {
+ Log.d(TAG, "EVENT_ON_LOST for " + network);
+ }
+ return mNetworkMap.remove(network);
+ }
+ default:
+ return null;
+ }
+ }
+ }
+
+ // Needed because the canonical source of upstream truth is just the
+ // upstream interface name, |mCurrentUpstreamIface|. This is ripe for
+ // future simplification, once the upstream Network is canonical.
+ boolean pertainsToCurrentUpstream(NetworkState ns) {
+ if (ns != null && ns.linkProperties != null && mCurrentUpstreamIface != null) {
+ for (String ifname : ns.linkProperties.getAllInterfaceNames()) {
+ if (mCurrentUpstreamIface.equals(ifname)) {
+ return true;
}
}
- return false;
}
-
- void processNetworkLost(Network network) {
- if (network != null) {
- mNetworkMap.remove(network);
- }
- }
+ return false;
}
class TetherMasterSM extends StateMachine {
@@ -1517,14 +1219,7 @@
static final int CMD_RETRY_UPSTREAM = BASE_MASTER + 4;
// Events from NetworkCallbacks that we process on the master state
// machine thread on behalf of the UpstreamNetworkMonitor.
- static final int EVENT_UPSTREAM_LINKPROPERTIES_CHANGED = BASE_MASTER + 5;
- static final int EVENT_UPSTREAM_LOST = BASE_MASTER + 6;
-
- // This indicates what a timeout event relates to. A state that
- // sends itself a delayed timeout event and handles incoming timeout events
- // should inc this when it is entered and whenever it sends a new timeout event.
- // We do not flush the old ones.
- private int mSequenceNumber;
+ static final int EVENT_UPSTREAM_CALLBACK = BASE_MASTER + 5;
private State mInitialState;
private State mTetherModeAliveState;
@@ -1535,7 +1230,20 @@
private State mStopTetheringErrorState;
private State mSetDnsForwardersErrorState;
- private ArrayList<TetherInterfaceSM> mNotifyList;
+ // This list is a little subtle. It contains all the interfaces that currently are
+ // requesting tethering, regardless of whether these interfaces are still members of
+ // mTetherStates. This allows us to maintain the following predicates:
+ //
+ // 1) mTetherStates contains the set of all currently existing, tetherable, link state up
+ // interfaces.
+ // 2) mNotifyList contains all state machines that may have outstanding tethering state
+ // that needs to be torn down.
+ //
+ // Because we excise interfaces immediately from mTetherStates, we must maintain mNotifyList
+ // so that the garbage collector does not clean up the state machine before it has a chance
+ // to tear itself down.
+ private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
+ private final IPv6TetheringCoordinator mIPv6TetheringCoordinator;
private int mMobileApnReserved = ConnectivityManager.TYPE_NONE;
private NetworkCallback mMobileUpstreamCallback;
@@ -1562,7 +1270,8 @@
mSetDnsForwardersErrorState = new SetDnsForwardersErrorState();
addState(mSetDnsForwardersErrorState);
- mNotifyList = new ArrayList<TetherInterfaceSM>();
+ mNotifyList = new ArrayList<>();
+ mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList);
setInitialState(mInitialState);
}
@@ -1667,6 +1376,7 @@
}
protected void chooseUpstreamType(boolean tryCell) {
+ final ConnectivityManager cm = getConnectivityManager();
int upType = ConnectivityManager.TYPE_NONE;
String iface = null;
@@ -1681,8 +1391,9 @@
}
for (Integer netType : mUpstreamIfaceTypes) {
- NetworkInfo info =
- getConnectivityManager().getNetworkInfo(netType.intValue());
+ NetworkInfo info = cm.getNetworkInfo(netType.intValue());
+ // TODO: if the network is suspended we should consider
+ // that to be the same as connected here.
if ((info != null) && info.isConnected()) {
upType = netType.intValue();
break;
@@ -1723,9 +1434,9 @@
break;
}
+ Network network = null;
if (upType != ConnectivityManager.TYPE_NONE) {
- LinkProperties linkProperties =
- getConnectivityManager().getLinkProperties(upType);
+ LinkProperties linkProperties = cm.getLinkProperties(upType);
if (linkProperties != null) {
// Find the interface with the default IPv4 route. It may be the
// interface described by linkProperties, or one of the interfaces
@@ -1742,7 +1453,7 @@
}
if (iface != null) {
- Network network = getConnectivityManager().getNetworkForType(upType);
+ network = cm.getNetworkForType(upType);
if (network == null) {
Log.e(TAG, "No Network for upstream type " + upType + "!");
}
@@ -1750,6 +1461,17 @@
}
}
notifyTetheredOfNewUpstreamIface(iface);
+ NetworkState ns = mUpstreamNetworkMonitor.lookup(network);
+ if (ns != null && pertainsToCurrentUpstream(ns)) {
+ // If we already have NetworkState for this network examine
+ // it immediately, because there likely will be no second
+ // EVENT_ON_AVAILABLE (it was already received).
+ handleNewUpstreamNetworkState(ns);
+ } else if (mCurrentUpstreamIface == null) {
+ // There are no available upstream networks, or none that
+ // have an IPv4 default route (current metric for success).
+ handleNewUpstreamNetworkState(null);
+ }
}
protected void setDnsForwarders(final Network network, final LinkProperties lp) {
@@ -1777,11 +1499,15 @@
protected void notifyTetheredOfNewUpstreamIface(String ifaceName) {
if (DBG) Log.d(TAG, "Notifying tethered with upstream=" + ifaceName);
mCurrentUpstreamIface = ifaceName;
- for (TetherInterfaceSM sm : mNotifyList) {
- sm.sendMessage(TetherInterfaceSM.CMD_TETHER_CONNECTION_CHANGED,
+ for (TetherInterfaceStateMachine sm : mNotifyList) {
+ sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
ifaceName);
}
}
+
+ protected void handleNewUpstreamNetworkState(NetworkState ns) {
+ mIPv6TetheringCoordinator.updateUpstreamNetworkState(ns);
+ }
}
private final AtomicInteger mSimBcastGenerationNumber = new AtomicInteger(0);
@@ -1845,20 +1571,16 @@
config_mobile_hotspot_provision_app_no_ui).isEmpty() == false) {
ArrayList<Integer> tethered = new ArrayList<Integer>();
synchronized (mPublicSync) {
- Set ifaces = mIfaces.keySet();
- for (Object iface : ifaces) {
- TetherInterfaceSM sm = mIfaces.get(iface);
- if (sm != null && sm.isTethered()) {
- if (isUsb((String)iface)) {
- tethered.add(new Integer(
- ConnectivityManager.TETHERING_USB));
- } else if (isWifi((String)iface)) {
- tethered.add(new Integer(
- ConnectivityManager.TETHERING_WIFI));
- } else if (isBluetooth((String)iface)) {
- tethered.add(new Integer(
- ConnectivityManager.TETHERING_BLUETOOTH));
- }
+ for (int i = 0; i < mTetherStates.size(); i++) {
+ TetherState tetherState = mTetherStates.valueAt(i);
+ if (tetherState.mLastState !=
+ IControlsTethering.STATE_TETHERED) {
+ continue; // Skip interfaces that aren't tethered.
+ }
+ String iface = mTetherStates.keyAt(i);
+ int interfaceType = ifaceNameToType(iface);
+ if (interfaceType != ConnectivityManager.TETHERING_INVALID) {
+ tethered.add(new Integer(interfaceType));
}
}
}
@@ -1885,26 +1607,24 @@
class InitialState extends TetherMasterUtilState {
@Override
- public void enter() {
- }
- @Override
public boolean processMessage(Message message) {
maybeLogMessage(this, message.what);
boolean retValue = true;
switch (message.what) {
case CMD_TETHER_MODE_REQUESTED:
- TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
+ TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj;
if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
- mNotifyList.add(who);
+ if (mNotifyList.indexOf(who) < 0) {
+ mNotifyList.add(who);
+ mIPv6TetheringCoordinator.addActiveDownstream(who);
+ }
transitionTo(mTetherModeAliveState);
break;
case CMD_TETHER_MODE_UNREQUESTED:
- who = (TetherInterfaceSM)message.obj;
+ who = (TetherInterfaceStateMachine)message.obj;
if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
- int index = mNotifyList.indexOf(who);
- if (index != -1) {
- mNotifyList.remove(who);
- }
+ mNotifyList.remove(who);
+ mIPv6TetheringCoordinator.removeActiveDownstream(who);
break;
default:
retValue = false;
@@ -1927,6 +1647,7 @@
chooseUpstreamType(mTryCell);
mTryCell = !mTryCell;
}
+
@Override
public void exit() {
// TODO: examine if we should check the return value.
@@ -1934,39 +1655,47 @@
mUpstreamNetworkMonitor.stop();
stopListeningForSimChanges();
notifyTetheredOfNewUpstreamIface(null);
+ handleNewUpstreamNetworkState(null);
}
+
@Override
public boolean processMessage(Message message) {
maybeLogMessage(this, message.what);
boolean retValue = true;
switch (message.what) {
- case CMD_TETHER_MODE_REQUESTED:
- TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
+ case CMD_TETHER_MODE_REQUESTED: {
+ TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj;
if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
- mNotifyList.add(who);
- who.sendMessage(TetherInterfaceSM.CMD_TETHER_CONNECTION_CHANGED,
+ if (mNotifyList.indexOf(who) < 0) {
+ mNotifyList.add(who);
+ mIPv6TetheringCoordinator.addActiveDownstream(who);
+ }
+ who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
mCurrentUpstreamIface);
break;
- case CMD_TETHER_MODE_UNREQUESTED:
- who = (TetherInterfaceSM)message.obj;
+ }
+ case CMD_TETHER_MODE_UNREQUESTED: {
+ TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj;
if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
- int index = mNotifyList.indexOf(who);
- if (index != -1) {
+ if (mNotifyList.remove(who)) {
if (DBG) Log.d(TAG, "TetherModeAlive removing notifyee " + who);
- mNotifyList.remove(index);
if (mNotifyList.isEmpty()) {
turnOffMasterTetherSettings(); // transitions appropriately
} else {
if (DBG) {
Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size() +
" live requests:");
- for (Object o : mNotifyList) Log.d(TAG, " " + o);
+ for (TetherInterfaceStateMachine o : mNotifyList) {
+ Log.d(TAG, " " + o);
+ }
}
}
} else {
Log.e(TAG, "TetherModeAliveState UNREQUESTED has unknown who: " + who);
}
+ mIPv6TetheringCoordinator.removeActiveDownstream(who);
break;
+ }
case CMD_UPSTREAM_CHANGED:
// need to try DUN immediately if Wifi goes down
mTryCell = true;
@@ -1977,24 +1706,56 @@
chooseUpstreamType(mTryCell);
mTryCell = !mTryCell;
break;
- case EVENT_UPSTREAM_LINKPROPERTIES_CHANGED:
- NetworkState state = (NetworkState) message.obj;
- if (mUpstreamNetworkMonitor.processLinkPropertiesChanged(state)) {
- setDnsForwarders(state.network, state.linkProperties);
- } else if (mCurrentUpstreamIface == null) {
- // If we have no upstream interface, try to run through upstream
- // selection again. If, for example, IPv4 connectivity has shown up
- // after IPv6 (e.g., 464xlat became available) we want the chance to
- // notice and act accordingly.
- chooseUpstreamType(false);
+ case EVENT_UPSTREAM_CALLBACK: {
+ // First: always update local state about every network.
+ final NetworkState ns = mUpstreamNetworkMonitor.processCallback(
+ message.arg1, message.obj);
+
+ if (ns == null || !pertainsToCurrentUpstream(ns)) {
+ // TODO: In future, this is where upstream evaluation and selection
+ // could be handled for notifications which include sufficient data.
+ // For example, after CONNECTIVITY_ACTION listening is removed, here
+ // is where we could observe a Wi-Fi network becoming available and
+ // passing validation.
+ if (mCurrentUpstreamIface == null) {
+ // If we have no upstream interface, try to run through upstream
+ // selection again. If, for example, IPv4 connectivity has shown up
+ // after IPv6 (e.g., 464xlat became available) we want the chance to
+ // notice and act accordingly.
+ chooseUpstreamType(false);
+ }
+ break;
+ }
+
+ switch (message.arg1) {
+ case UpstreamNetworkMonitor.EVENT_ON_AVAILABLE:
+ // The default network changed, or DUN connected
+ // before this callback was processed. Updates
+ // for the current NetworkCapabilities and
+ // LinkProperties have been requested (default
+ // request) or are being sent shortly (DUN). Do
+ // nothing until they arrive; if no updates
+ // arrive there's nothing to do.
+ break;
+ case UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES:
+ handleNewUpstreamNetworkState(ns);
+ break;
+ case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES:
+ setDnsForwarders(ns.network, ns.linkProperties);
+ handleNewUpstreamNetworkState(ns);
+ break;
+ case UpstreamNetworkMonitor.EVENT_ON_LOST:
+ // TODO: Re-evaluate possible upstreams. Currently upstream
+ // reevaluation is triggered via received CONNECTIVITY_ACTION
+ // broadcasts that result in being passed a
+ // TetherMasterSM.CMD_UPSTREAM_CHANGED.
+ handleNewUpstreamNetworkState(null);
+ break;
+ default:
+ break;
}
break;
- case EVENT_UPSTREAM_LOST:
- // TODO: Re-evaluate possible upstreams. Currently upstream reevaluation
- // is triggered via received CONNECTIVITY_ACTION broadcasts that result
- // in being passed a TetherMasterSM.CMD_UPSTREAM_CHANGED.
- mUpstreamNetworkMonitor.processNetworkLost((Network) message.obj);
- break;
+ }
default:
retValue = false;
break;
@@ -2010,7 +1771,7 @@
boolean retValue = true;
switch (message.what) {
case CMD_TETHER_MODE_REQUESTED:
- TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
+ TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj;
who.sendMessage(mErrorNotification);
break;
default:
@@ -2020,8 +1781,7 @@
}
void notify(int msgType) {
mErrorNotification = msgType;
- for (Object o : mNotifyList) {
- TetherInterfaceSM sm = (TetherInterfaceSM)o;
+ for (TetherInterfaceStateMachine sm : mNotifyList) {
sm.sendMessage(msgType);
}
}
@@ -2031,7 +1791,7 @@
@Override
public void enter() {
Log.e(TAG, "Error in setIpForwardingEnabled");
- notify(TetherInterfaceSM.CMD_IP_FORWARDING_ENABLE_ERROR);
+ notify(TetherInterfaceStateMachine.CMD_IP_FORWARDING_ENABLE_ERROR);
}
}
@@ -2039,7 +1799,7 @@
@Override
public void enter() {
Log.e(TAG, "Error in setIpForwardingDisabled");
- notify(TetherInterfaceSM.CMD_IP_FORWARDING_DISABLE_ERROR);
+ notify(TetherInterfaceStateMachine.CMD_IP_FORWARDING_DISABLE_ERROR);
}
}
@@ -2047,7 +1807,7 @@
@Override
public void enter() {
Log.e(TAG, "Error in startTethering");
- notify(TetherInterfaceSM.CMD_START_TETHERING_ERROR);
+ notify(TetherInterfaceStateMachine.CMD_START_TETHERING_ERROR);
try {
mNMService.setIpForwardingEnabled(false);
} catch (Exception e) {}
@@ -2058,7 +1818,7 @@
@Override
public void enter() {
Log.e(TAG, "Error in stopTethering");
- notify(TetherInterfaceSM.CMD_STOP_TETHERING_ERROR);
+ notify(TetherInterfaceStateMachine.CMD_STOP_TETHERING_ERROR);
try {
mNMService.setIpForwardingEnabled(false);
} catch (Exception e) {}
@@ -2069,7 +1829,7 @@
@Override
public void enter() {
Log.e(TAG, "Error in setDnsForwarders");
- notify(TetherInterfaceSM.CMD_SET_DNS_FORWARDERS_ERROR);
+ notify(TetherInterfaceStateMachine.CMD_SET_DNS_FORWARDERS_ERROR);
try {
mNMService.stopTethering();
} catch (Exception e) {}
@@ -2080,9 +1840,11 @@
}
}
+ @Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ // Binder.java closes the resource for us.
+ @SuppressWarnings("resource")
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
-
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump ConnectivityService.Tether " +
@@ -2102,12 +1864,67 @@
pw.println("Tether state:");
pw.increaseIndent();
- for (Object o : mIfaces.values()) {
- pw.println(o);
+ for (int i = 0; i < mTetherStates.size(); i++) {
+ final String iface = mTetherStates.keyAt(i);
+ final TetherState tetherState = mTetherStates.valueAt(i);
+ pw.print(iface + " - ");
+
+ switch (tetherState.mLastState) {
+ case IControlsTethering.STATE_UNAVAILABLE:
+ pw.print("UnavailableState");
+ break;
+ case IControlsTethering.STATE_AVAILABLE:
+ pw.print("AvailableState");
+ break;
+ case IControlsTethering.STATE_TETHERED:
+ pw.print("TetheredState");
+ break;
+ default:
+ pw.print("UnknownState");
+ break;
+ }
+ pw.println(" - lastError = " + tetherState.mLastError);
}
pw.decreaseIndent();
}
pw.decreaseIndent();
- return;
+ }
+
+ @Override
+ public void notifyInterfaceStateChange(String iface, TetherInterfaceStateMachine who,
+ int state, int error) {
+ synchronized (mPublicSync) {
+ TetherState tetherState = mTetherStates.get(iface);
+ if (tetherState != null && tetherState.mStateMachine.equals(who)) {
+ tetherState.mLastState = state;
+ tetherState.mLastError = error;
+ } else {
+ if (DBG) Log.d(TAG, "got notification from stale iface " + iface);
+ }
+ }
+
+ if (DBG) {
+ Log.d(TAG, "iface " + iface + " notified that it was in state " + state +
+ " with error " + error);
+ }
+
+ switch (state) {
+ case IControlsTethering.STATE_UNAVAILABLE:
+ case IControlsTethering.STATE_AVAILABLE:
+ mTetherMasterSM.sendMessage(TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED, who);
+ break;
+ case IControlsTethering.STATE_TETHERED:
+ mTetherMasterSM.sendMessage(TetherMasterSM.CMD_TETHER_MODE_REQUESTED, who);
+ break;
+ }
+ sendTetherStateChangedBroadcast();
+ }
+
+ private void trackNewTetherableInterface(String iface, int interfaceType) {
+ TetherState tetherState;
+ tetherState = new TetherState(new TetherInterfaceStateMachine(iface, mLooper,
+ interfaceType, mNMService, mStatsService, this));
+ mTetherStates.put(iface, tetherState);
+ tetherState.mStateMachine.start();
}
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java b/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java
new file mode 100644
index 0000000..449b8a8
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+/**
+ * @hide
+ *
+ * Interface with methods necessary to notify that a given interface is ready for tethering.
+ */
+public interface IControlsTethering {
+ public final int STATE_UNAVAILABLE = 0;
+ public final int STATE_AVAILABLE = 1;
+ public final int STATE_TETHERED = 2;
+
+ /**
+ * Notify that |who| has changed its tethering state. This may be called from any thread.
+ *
+ * @param iface a network interface (e.g. "wlan0")
+ * @param who corresponding instance of a TetherInterfaceStateMachine
+ * @param state one of IControlsTethering.STATE_*
+ * @param lastError one of ConnectivityManager.TETHER_ERROR_*
+ */
+ void notifyInterfaceStateChange(String iface, TetherInterfaceStateMachine who,
+ int state, int lastError);
+}
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
new file mode 100644
index 0000000..9173feb
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+import android.net.ConnectivityManager;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkState;
+import android.net.RouteInfo;
+import android.util.Log;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+
+/**
+ * IPv6 tethering is rather different from IPv4 owing to the absence of NAT.
+ * This coordinator is responsible for evaluating the dedicated prefixes
+ * assigned to the device and deciding how to divvy them up among downstream
+ * interfaces.
+ *
+ * @hide
+ */
+public class IPv6TetheringCoordinator {
+ private static final String TAG = IPv6TetheringCoordinator.class.getSimpleName();
+ private static final boolean DBG = false;
+ private static final boolean VDBG = false;
+
+ private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
+ private final LinkedList<TetherInterfaceStateMachine> mActiveDownstreams;
+ private NetworkState mUpstreamNetworkState;
+
+ public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList) {
+ mNotifyList = notifyList;
+ mActiveDownstreams = new LinkedList<>();
+ }
+
+ public void addActiveDownstream(TetherInterfaceStateMachine downstream) {
+ if (mActiveDownstreams.indexOf(downstream) == -1) {
+ // Adding a new downstream appends it to the list. Adding a
+ // downstream a second time without first removing it has no effect.
+ mActiveDownstreams.offer(downstream);
+ updateIPv6TetheringInterfaces();
+ }
+ }
+
+ public void removeActiveDownstream(TetherInterfaceStateMachine downstream) {
+ stopIPv6TetheringOn(downstream);
+ if (mActiveDownstreams.remove(downstream)) {
+ updateIPv6TetheringInterfaces();
+ }
+ }
+
+ public void updateUpstreamNetworkState(NetworkState ns) {
+ if (VDBG) {
+ Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
+ }
+ if (!canTetherIPv6(ns)) {
+ stopIPv6TetheringOnAllInterfaces();
+ setUpstreamNetworkState(null);
+ return;
+ }
+
+ if (mUpstreamNetworkState != null &&
+ !ns.network.equals(mUpstreamNetworkState.network)) {
+ stopIPv6TetheringOnAllInterfaces();
+ }
+
+ setUpstreamNetworkState(ns);
+ updateIPv6TetheringInterfaces();
+ }
+
+ private void stopIPv6TetheringOnAllInterfaces() {
+ for (TetherInterfaceStateMachine sm : mNotifyList) {
+ stopIPv6TetheringOn(sm);
+ }
+ }
+
+ private void setUpstreamNetworkState(NetworkState ns) {
+ if (ns == null) {
+ mUpstreamNetworkState = null;
+ } else {
+ // Make a deep copy of the parts we need.
+ mUpstreamNetworkState = new NetworkState(
+ null,
+ new LinkProperties(ns.linkProperties),
+ new NetworkCapabilities(ns.networkCapabilities),
+ new Network(ns.network),
+ null,
+ null);
+ }
+
+ if (DBG) {
+ Log.d(TAG, "setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState));
+ }
+ }
+
+ private void updateIPv6TetheringInterfaces() {
+ for (TetherInterfaceStateMachine sm : mNotifyList) {
+ final LinkProperties lp = getInterfaceIPv6LinkProperties(sm);
+ sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
+ break;
+ }
+ }
+
+ private LinkProperties getInterfaceIPv6LinkProperties(TetherInterfaceStateMachine sm) {
+ if (mUpstreamNetworkState == null) return null;
+
+ if (sm.interfaceType() == ConnectivityManager.TETHERING_BLUETOOTH) {
+ // TODO: Figure out IPv6 support on PAN interfaces.
+ return null;
+ }
+
+ // NOTE: Here, in future, we would have policies to decide how to divvy
+ // up the available dedicated prefixes among downstream interfaces.
+ // At this time we have no such mechanism--we only support tethering
+ // IPv6 toward the oldest (first requested) active downstream.
+
+ final TetherInterfaceStateMachine currentActive = mActiveDownstreams.peek();
+ if (currentActive != null && currentActive == sm) {
+ final LinkProperties lp = getIPv6OnlyLinkProperties(
+ mUpstreamNetworkState.linkProperties);
+ if (lp.hasIPv6DefaultRoute() && lp.hasGlobalIPv6Address()) {
+ return lp;
+ }
+ }
+
+ return null;
+ }
+
+ private static boolean canTetherIPv6(NetworkState ns) {
+ // Broadly speaking:
+ //
+ // [1] does the upstream have an IPv6 default route?
+ //
+ // and
+ //
+ // [2] does the upstream have one or more global IPv6 /64s
+ // dedicated to this device?
+ //
+ // In lieu of Prefix Delegation and other evaluation of whether a
+ // prefix may or may not be dedicated to this device, for now just
+ // check whether the upstream is TRANSPORT_CELLULAR. This works
+ // because "[t]he 3GPP network allocates each default bearer a unique
+ // /64 prefix", per RFC 6459, Section 5.2.
+
+ final boolean canTether =
+ (ns != null) && (ns.network != null) &&
+ (ns.linkProperties != null) && (ns.networkCapabilities != null) &&
+ // At least one upstream DNS server:
+ ns.linkProperties.isProvisioned() &&
+ // Minimal amount of IPv6 provisioning:
+ ns.linkProperties.hasIPv6DefaultRoute() &&
+ ns.linkProperties.hasGlobalIPv6Address() &&
+ // Temporary approximation of "dedicated prefix":
+ ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
+
+ // For now, we do not support separate IPv4 and IPv6 upstreams (e.g.
+ // tethering with 464xlat involved). TODO: Rectify this shortcoming,
+ // likely by calling NetworkManagementService#startInterfaceForwarding()
+ // for all upstream interfaces.
+ RouteInfo v4default = null;
+ RouteInfo v6default = null;
+ if (canTether) {
+ for (RouteInfo r : ns.linkProperties.getAllRoutes()) {
+ if (r.isIPv4Default()) {
+ v4default = r;
+ } else if (r.isIPv6Default()) {
+ v6default = r;
+ }
+
+ if (v4default != null && v6default != null) {
+ break;
+ }
+ }
+ }
+
+ final boolean supportedConfiguration =
+ (v4default != null) && (v6default != null) &&
+ (v4default.getInterface() != null) &&
+ v4default.getInterface().equals(v6default.getInterface());
+
+ final boolean outcome = canTether && supportedConfiguration;
+
+ if (VDBG) {
+ if (ns == null) {
+ Log.d(TAG, "No available upstream.");
+ } else {
+ Log.d(TAG, String.format("IPv6 tethering is %s for upstream: %s",
+ (outcome ? "available" : "not available"), toDebugString(ns)));
+ }
+ }
+
+ return outcome;
+ }
+
+ private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) {
+ final LinkProperties v6only = new LinkProperties();
+ if (lp == null) {
+ return v6only;
+ }
+
+ // NOTE: At this time we don't copy over any information about any
+ // stacked links. No current stacked link configuration has IPv6.
+
+ v6only.setInterfaceName(lp.getInterfaceName());
+
+ v6only.setMtu(lp.getMtu());
+
+ for (LinkAddress linkAddr : lp.getLinkAddresses()) {
+ if (linkAddr.isGlobalPreferred() && linkAddr.getPrefixLength() == 64) {
+ v6only.addLinkAddress(linkAddr);
+ }
+ }
+
+ for (RouteInfo routeInfo : lp.getRoutes()) {
+ final IpPrefix destination = routeInfo.getDestination();
+ if ((destination.getAddress() instanceof Inet6Address) &&
+ (destination.getPrefixLength() <= 64)) {
+ v6only.addRoute(routeInfo);
+ }
+ }
+
+ for (InetAddress dnsServer : lp.getDnsServers()) {
+ if (isIPv6GlobalAddress(dnsServer)) {
+ // For now we include ULAs.
+ v6only.addDnsServer(dnsServer);
+ }
+ }
+
+ v6only.setDomains(lp.getDomains());
+
+ return v6only;
+ }
+
+ // TODO: Delete this and switch to LinkAddress#isGlobalPreferred once we
+ // announce our own IPv6 address as DNS server.
+ private static boolean isIPv6GlobalAddress(InetAddress ip) {
+ return (ip instanceof Inet6Address) &&
+ !ip.isAnyLocalAddress() &&
+ !ip.isLoopbackAddress() &&
+ !ip.isLinkLocalAddress() &&
+ !ip.isSiteLocalAddress() &&
+ !ip.isMulticastAddress();
+ }
+
+ private static String toDebugString(NetworkState ns) {
+ if (ns == null) {
+ return "NetworkState{null}";
+ }
+ return String.format("NetworkState{%s, %s, %s}",
+ ns.network,
+ ns.networkCapabilities,
+ ns.linkProperties);
+ }
+
+ private static void stopIPv6TetheringOn(TetherInterfaceStateMachine sm) {
+ sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
new file mode 100644
index 0000000..7525f30
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+import android.net.INetd;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
+import android.net.NetworkState;
+import android.net.RouteInfo;
+import android.net.ip.RouterAdvertisementDaemon;
+import android.net.ip.RouterAdvertisementDaemon.RaParams;
+import android.os.INetworkManagementService;
+import android.os.ServiceSpecificException;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Objects;
+
+
+/**
+ * @hide
+ */
+class IPv6TetheringInterfaceServices {
+ private static final String TAG = IPv6TetheringInterfaceServices.class.getSimpleName();
+ private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64");
+ private static final int RFC7421_IP_PREFIX_LENGTH = 64;
+
+ private final String mIfName;
+ private final INetworkManagementService mNMService;
+
+ private NetworkInterface mNetworkInterface;
+ private byte[] mHwAddr;
+ private LinkProperties mLastIPv6LinkProperties;
+ private RouterAdvertisementDaemon mRaDaemon;
+ private RaParams mLastRaParams;
+
+ IPv6TetheringInterfaceServices(String ifname, INetworkManagementService nms) {
+ mIfName = ifname;
+ mNMService = nms;
+ }
+
+ public boolean start() {
+ try {
+ mNetworkInterface = NetworkInterface.getByName(mIfName);
+ } catch (SocketException e) {
+ Log.e(TAG, "Failed to find NetworkInterface for " + mIfName, e);
+ stop();
+ return false;
+ }
+
+ try {
+ mHwAddr = mNetworkInterface.getHardwareAddress();
+ } catch (SocketException e) {
+ Log.e(TAG, "Failed to find hardware address for " + mIfName, e);
+ stop();
+ return false;
+ }
+
+ final int ifindex = mNetworkInterface.getIndex();
+ mRaDaemon = new RouterAdvertisementDaemon(mIfName, ifindex, mHwAddr);
+ if (!mRaDaemon.start()) {
+ stop();
+ return false;
+ }
+
+ return true;
+ }
+
+ public void stop() {
+ mNetworkInterface = null;
+ mHwAddr = null;
+ setRaParams(null);
+
+ if (mRaDaemon != null) {
+ mRaDaemon.stop();
+ mRaDaemon = null;
+ }
+ }
+
+ // IPv6TetheringCoordinator sends updates with carefully curated IPv6-only
+ // LinkProperties. These have extraneous data filtered out and only the
+ // necessary prefixes included (per its prefix distribution policy).
+ //
+ // TODO: Evaluate using a data structure than is more directly suited to
+ // communicating only the relevant information.
+ public void updateUpstreamIPv6LinkProperties(LinkProperties v6only) {
+ if (mRaDaemon == null) return;
+
+ // Avoid unnecessary work on spurious updates.
+ if (Objects.equals(mLastIPv6LinkProperties, v6only)) {
+ return;
+ }
+
+ RaParams params = null;
+
+ if (v6only != null) {
+ params = new RaParams();
+ params.mtu = v6only.getMtu();
+ params.hasDefaultRoute = v6only.hasIPv6DefaultRoute();
+
+ for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
+ if (linkAddr.getPrefixLength() != RFC7421_IP_PREFIX_LENGTH) continue;
+
+ final IpPrefix prefix = new IpPrefix(
+ linkAddr.getAddress(), linkAddr.getPrefixLength());
+ params.prefixes.add(prefix);
+
+ final Inet6Address dnsServer = getLocalDnsIpFor(prefix);
+ if (dnsServer != null) {
+ params.dnses.add(dnsServer);
+ }
+ }
+ }
+ // If v6only is null, we pass in null to setRaParams(), which handles
+ // deprecation of any existing RA data.
+
+ setRaParams(params);
+ mLastIPv6LinkProperties = v6only;
+ }
+
+
+ private void configureLocalRoutes(
+ HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) {
+ // [1] Remove the routes that are deprecated.
+ if (!deprecatedPrefixes.isEmpty()) {
+ final ArrayList<RouteInfo> toBeRemoved = getLocalRoutesFor(deprecatedPrefixes);
+ try {
+ final int removalFailures = mNMService.removeRoutesFromLocalNetwork(toBeRemoved);
+ if (removalFailures > 0) {
+ Log.e(TAG, String.format("Failed to remove %d IPv6 routes from local table.",
+ removalFailures));
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to remove IPv6 routes from local table: ", e);
+ }
+ }
+
+ // [2] Add only the routes that have not previously been added.
+ if (newPrefixes != null && !newPrefixes.isEmpty()) {
+ HashSet<IpPrefix> addedPrefixes = (HashSet) newPrefixes.clone();
+ if (mLastRaParams != null) {
+ addedPrefixes.removeAll(mLastRaParams.prefixes);
+ }
+
+ if (mLastRaParams == null || mLastRaParams.prefixes.isEmpty()) {
+ // We need to be able to send unicast RAs, and clients might
+ // like to ping the default router's link-local address. Note
+ // that we never remove the link-local route from the network
+ // until Tethering disables tethering on the interface. We
+ // only need to add the link-local prefix once, but in the
+ // event we add it more than once netd silently ignores EEXIST.
+ addedPrefixes.add(LINK_LOCAL_PREFIX);
+ }
+
+ if (!addedPrefixes.isEmpty()) {
+ final ArrayList<RouteInfo> toBeAdded = getLocalRoutesFor(addedPrefixes);
+ try {
+ // It's safe to call addInterfaceToLocalNetwork() even if
+ // the interface is already in the local_network. Note also
+ // that adding routes that already exist does not cause an
+ // error (EEXIST is silently ignored).
+ mNMService.addInterfaceToLocalNetwork(mIfName, toBeAdded);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to add IPv6 routes to local table: ", e);
+ }
+ }
+ }
+ }
+
+ private void configureLocalDns(
+ HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) {
+ INetd netd = getNetdServiceOrNull();
+ if (netd == null) {
+ if (newDnses != null) newDnses.clear();
+ Log.e(TAG, "No netd service instance available; not setting local IPv6 addresses");
+ return;
+ }
+
+ // [1] Remove deprecated local DNS IP addresses.
+ if (!deprecatedDnses.isEmpty()) {
+ for (Inet6Address dns : deprecatedDnses) {
+ final String dnsString = dns.getHostAddress();
+ try {
+ netd.interfaceDelAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH);
+ } catch (ServiceSpecificException | RemoteException e) {
+ Log.e(TAG, "Failed to remove local dns IP: " + dnsString, e);
+ }
+ }
+ }
+
+ // [2] Add only the local DNS IP addresses that have not previously been added.
+ if (newDnses != null && !newDnses.isEmpty()) {
+ final HashSet<Inet6Address> addedDnses = (HashSet) newDnses.clone();
+ if (mLastRaParams != null) {
+ addedDnses.removeAll(mLastRaParams.dnses);
+ }
+
+ for (Inet6Address dns : addedDnses) {
+ final String dnsString = dns.getHostAddress();
+ try {
+ netd.interfaceAddAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH);
+ } catch (ServiceSpecificException | RemoteException e) {
+ Log.e(TAG, "Failed to add local dns IP: " + dnsString, e);
+ newDnses.remove(dns);
+ }
+ }
+ }
+
+ try {
+ netd.tetherApplyDnsInterfaces();
+ } catch (ServiceSpecificException | RemoteException e) {
+ Log.e(TAG, "Failed to update local DNS caching server");
+ if (newDnses != null) newDnses.clear();
+ }
+ }
+
+ private void setRaParams(RaParams newParams) {
+ if (mRaDaemon != null) {
+ final RaParams deprecatedParams =
+ RaParams.getDeprecatedRaParams(mLastRaParams, newParams);
+
+ configureLocalRoutes(deprecatedParams.prefixes,
+ (newParams != null) ? newParams.prefixes : null);
+
+ configureLocalDns(deprecatedParams.dnses,
+ (newParams != null) ? newParams.dnses : null);
+
+ mRaDaemon.buildNewRa(deprecatedParams, newParams);
+ }
+
+ mLastRaParams = newParams;
+ }
+
+ // Accumulate routes representing "prefixes to be assigned to the local
+ // interface", for subsequent modification of local_network routing.
+ private ArrayList<RouteInfo> getLocalRoutesFor(HashSet<IpPrefix> prefixes) {
+ final ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>();
+ for (IpPrefix ipp : prefixes) {
+ localRoutes.add(new RouteInfo(ipp, null, mIfName));
+ }
+ return localRoutes;
+ }
+
+ private INetd getNetdServiceOrNull() {
+ if (mNMService != null) {
+ try {
+ return mNMService.getNetdService();
+ } catch (RemoteException ignored) {
+ // This blocks until netd can be reached, but it can return
+ // null during a netd crash.
+ }
+ }
+ return null;
+ }
+
+ // Given a prefix like 2001:db8::/64 return 2001:db8::1.
+ private static Inet6Address getLocalDnsIpFor(IpPrefix localPrefix) {
+ final byte[] dnsBytes = localPrefix.getRawAddress();
+ dnsBytes[dnsBytes.length - 1] = 0x1;
+ try {
+ return Inet6Address.getByAddress(null, dnsBytes, 0);
+ } catch (UnknownHostException e) {
+ Slog.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix);
+ return null;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
new file mode 100644
index 0000000..9e7cb93
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+import android.net.ConnectivityManager;
+import android.net.INetworkStatsService;
+import android.net.InterfaceConfiguration;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkUtils;
+import android.os.INetworkManagementService;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.util.MessageUtils;
+import com.android.internal.util.Protocol;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import java.net.InetAddress;
+
+/**
+ * @hide
+ *
+ * Tracks the eligibility of a given network interface for tethering.
+ */
+public class TetherInterfaceStateMachine extends StateMachine {
+ private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129";
+ private static final int USB_PREFIX_LENGTH = 24;
+ private static final String WIFI_HOST_IFACE_ADDR = "192.168.43.1";
+ private static final int WIFI_HOST_IFACE_PREFIX_LENGTH = 24;
+
+ private final static String TAG = "TetherInterfaceSM";
+ private final static boolean DBG = false;
+ private final static boolean VDBG = false;
+ private static final Class[] messageClasses = {
+ TetherInterfaceStateMachine.class
+ };
+ private static final SparseArray<String> sMagicDecoderRing =
+ MessageUtils.findMessageNames(messageClasses);
+
+ private static final int BASE_IFACE = Protocol.BASE_TETHERING + 100;
+ // request from the user that it wants to tether
+ public static final int CMD_TETHER_REQUESTED = BASE_IFACE + 2;
+ // request from the user that it wants to untether
+ public static final int CMD_TETHER_UNREQUESTED = BASE_IFACE + 3;
+ // notification that this interface is down
+ public static final int CMD_INTERFACE_DOWN = BASE_IFACE + 4;
+ // notification from the master SM that it had trouble enabling IP Forwarding
+ public static final int CMD_IP_FORWARDING_ENABLE_ERROR = BASE_IFACE + 7;
+ // notification from the master SM that it had trouble disabling IP Forwarding
+ public static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IFACE + 8;
+ // notification from the master SM that it had trouble starting tethering
+ public static final int CMD_START_TETHERING_ERROR = BASE_IFACE + 9;
+ // notification from the master SM that it had trouble stopping tethering
+ public static final int CMD_STOP_TETHERING_ERROR = BASE_IFACE + 10;
+ // notification from the master SM that it had trouble setting the DNS forwarders
+ public static final int CMD_SET_DNS_FORWARDERS_ERROR = BASE_IFACE + 11;
+ // the upstream connection has changed
+ public static final int CMD_TETHER_CONNECTION_CHANGED = BASE_IFACE + 12;
+ // new IPv6 tethering parameters need to be processed
+ public static final int CMD_IPV6_TETHER_UPDATE = BASE_IFACE + 13;
+
+ private final State mInitialState;
+ private final State mTetheredState;
+ private final State mUnavailableState;
+
+ private final INetworkManagementService mNMService;
+ private final INetworkStatsService mStatsService;
+ private final IControlsTethering mTetherController;
+
+ private final String mIfaceName;
+ private final int mInterfaceType;
+ private final IPv6TetheringInterfaceServices mIPv6TetherSvc;
+
+ private int mLastError;
+ private String mMyUpstreamIfaceName; // may change over time
+
+ public TetherInterfaceStateMachine(String ifaceName, Looper looper, int interfaceType,
+ INetworkManagementService nMService, INetworkStatsService statsService,
+ IControlsTethering tetherController) {
+ super(ifaceName, looper);
+ mNMService = nMService;
+ mStatsService = statsService;
+ mTetherController = tetherController;
+ mIfaceName = ifaceName;
+ mInterfaceType = interfaceType;
+ mIPv6TetherSvc = new IPv6TetheringInterfaceServices(mIfaceName, mNMService);
+ mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
+
+ mInitialState = new InitialState();
+ addState(mInitialState);
+ mTetheredState = new TetheredState();
+ addState(mTetheredState);
+ mUnavailableState = new UnavailableState();
+ addState(mUnavailableState);
+
+ setInitialState(mInitialState);
+ }
+
+ public int interfaceType() {
+ return mInterfaceType;
+ }
+
+ // configured when we start tethering and unconfig'd on error or conclusion
+ private boolean configureIfaceIp(boolean enabled) {
+ if (VDBG) Log.d(TAG, "configureIfaceIp(" + enabled + ")");
+
+ String ipAsString = null;
+ int prefixLen = 0;
+ if (mInterfaceType == ConnectivityManager.TETHERING_USB) {
+ ipAsString = USB_NEAR_IFACE_ADDR;
+ prefixLen = USB_PREFIX_LENGTH;
+ } else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
+ ipAsString = WIFI_HOST_IFACE_ADDR;
+ prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH;
+ } else {
+ // Nothing to do, BT does this elsewhere.
+ return true;
+ }
+
+ InterfaceConfiguration ifcg = null;
+ try {
+ ifcg = mNMService.getInterfaceConfig(mIfaceName);
+ if (ifcg != null) {
+ InetAddress addr = NetworkUtils.numericToInetAddress(ipAsString);
+ ifcg.setLinkAddress(new LinkAddress(addr, prefixLen));
+ if (enabled) {
+ ifcg.setInterfaceUp();
+ } else {
+ ifcg.setInterfaceDown();
+ }
+ ifcg.clearFlag("running");
+ mNMService.setInterfaceConfig(mIfaceName, ifcg);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error configuring interface " + mIfaceName, e);
+ return false;
+ }
+
+ return true;
+ }
+
+ private void maybeLogMessage(State state, int what) {
+ if (DBG) {
+ Log.d(TAG, state.getName() + " got " +
+ sMagicDecoderRing.get(what, Integer.toString(what)));
+ }
+ }
+
+ class InitialState extends State {
+ @Override
+ public void enter() {
+ mTetherController.notifyInterfaceStateChange(
+ mIfaceName, TetherInterfaceStateMachine.this,
+ IControlsTethering.STATE_AVAILABLE, mLastError);
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ maybeLogMessage(this, message.what);
+ boolean retValue = true;
+ switch (message.what) {
+ case CMD_TETHER_REQUESTED:
+ mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
+ transitionTo(mTetheredState);
+ break;
+ case CMD_INTERFACE_DOWN:
+ transitionTo(mUnavailableState);
+ break;
+ case CMD_IPV6_TETHER_UPDATE:
+ mIPv6TetherSvc.updateUpstreamIPv6LinkProperties(
+ (LinkProperties) message.obj);
+ break;
+ default:
+ retValue = false;
+ break;
+ }
+ return retValue;
+ }
+ }
+
+ class TetheredState extends State {
+ @Override
+ public void enter() {
+ if (!configureIfaceIp(true)) {
+ mLastError = ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR;
+ transitionTo(mInitialState);
+ return;
+ }
+
+ try {
+ mNMService.tetherInterface(mIfaceName);
+ } catch (Exception e) {
+ Log.e(TAG, "Error Tethering: " + e.toString());
+ mLastError = ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
+ transitionTo(mInitialState);
+ return;
+ }
+
+ if (!mIPv6TetherSvc.start()) {
+ Log.e(TAG, "Failed to start IPv6TetheringInterfaceServices");
+ }
+
+ if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
+ mTetherController.notifyInterfaceStateChange(
+ mIfaceName, TetherInterfaceStateMachine.this,
+ IControlsTethering.STATE_TETHERED, mLastError);
+ }
+
+ @Override
+ public void exit() {
+ // Note that at this point, we're leaving the tethered state. We can fail any
+ // of these operations, but it doesn't really change that we have to try them
+ // all in sequence.
+ mIPv6TetherSvc.stop();
+ cleanupUpstream();
+
+ try {
+ mNMService.untetherInterface(mIfaceName);
+ } catch (Exception ee) {
+ mLastError = ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
+ Log.e(TAG, "Failed to untether interface: " + ee.toString());
+ }
+
+ configureIfaceIp(false);
+ }
+
+ private void cleanupUpstream() {
+ if (mMyUpstreamIfaceName != null) {
+ // note that we don't care about errors here.
+ // sometimes interfaces are gone before we get
+ // to remove their rules, which generates errors.
+ // just do the best we can.
+ try {
+ // about to tear down NAT; gather remaining statistics
+ mStatsService.forceUpdate();
+ } catch (Exception e) {
+ if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString());
+ }
+ try {
+ mNMService.stopInterfaceForwarding(mIfaceName, mMyUpstreamIfaceName);
+ } catch (Exception e) {
+ if (VDBG) Log.e(
+ TAG, "Exception in removeInterfaceForward: " + e.toString());
+ }
+ try {
+ mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
+ } catch (Exception e) {
+ if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString());
+ }
+ mMyUpstreamIfaceName = null;
+ }
+ return;
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ maybeLogMessage(this, message.what);
+ boolean retValue = true;
+ switch (message.what) {
+ case CMD_TETHER_UNREQUESTED:
+ transitionTo(mInitialState);
+ if (DBG) Log.d(TAG, "Untethered (unrequested)" + mIfaceName);
+ break;
+ case CMD_INTERFACE_DOWN:
+ transitionTo(mUnavailableState);
+ if (DBG) Log.d(TAG, "Untethered (ifdown)" + mIfaceName);
+ break;
+ case CMD_TETHER_CONNECTION_CHANGED:
+ String newUpstreamIfaceName = (String)(message.obj);
+ if ((mMyUpstreamIfaceName == null && newUpstreamIfaceName == null) ||
+ (mMyUpstreamIfaceName != null &&
+ mMyUpstreamIfaceName.equals(newUpstreamIfaceName))) {
+ if (VDBG) Log.d(TAG, "Connection changed noop - dropping");
+ break;
+ }
+ cleanupUpstream();
+ if (newUpstreamIfaceName != null) {
+ try {
+ mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
+ mNMService.startInterfaceForwarding(mIfaceName,
+ newUpstreamIfaceName);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception enabling Nat: " + e.toString());
+ mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
+ transitionTo(mInitialState);
+ return true;
+ }
+ }
+ mMyUpstreamIfaceName = newUpstreamIfaceName;
+ break;
+ case CMD_IPV6_TETHER_UPDATE:
+ mIPv6TetherSvc.updateUpstreamIPv6LinkProperties(
+ (LinkProperties) message.obj);
+ break;
+ case CMD_IP_FORWARDING_ENABLE_ERROR:
+ case CMD_IP_FORWARDING_DISABLE_ERROR:
+ case CMD_START_TETHERING_ERROR:
+ case CMD_STOP_TETHERING_ERROR:
+ case CMD_SET_DNS_FORWARDERS_ERROR:
+ mLastError = ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
+ transitionTo(mInitialState);
+ break;
+ default:
+ retValue = false;
+ break;
+ }
+ return retValue;
+ }
+ }
+
+ /**
+ * This state is terminal for the per interface state machine. At this
+ * point, the master state machine should have removed this interface
+ * specific state machine from its list of possible recipients of
+ * tethering requests. The state machine itself will hang around until
+ * the garbage collector finds it.
+ */
+ class UnavailableState extends State {
+ @Override
+ public void enter() {
+ mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
+ mTetherController.notifyInterfaceStateChange(
+ mIfaceName, TetherInterfaceStateMachine.this,
+ IControlsTethering.STATE_UNAVAILABLE, mLastError);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index c16dac2..15ae846 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -22,6 +22,7 @@
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
+import android.annotation.Nullable;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -268,7 +269,7 @@
pw.println();
pw.println("Automatic Brightness Controller State:");
pw.println(" mLightSensor=" + mLightSensor);
- pw.println(" mTwilight.getCurrentState()=" + mTwilight.getCurrentState());
+ pw.println(" mTwilight.getLastTwilightState()=" + mTwilight.getLastTwilightState());
pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
pw.println(" mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
pw.println(" mAmbientLux=" + mAmbientLux);
@@ -495,12 +496,14 @@
}
if (mUseTwilight) {
- TwilightState state = mTwilight.getCurrentState();
+ TwilightState state = mTwilight.getLastTwilightState();
if (state != null && state.isNight()) {
- final long now = System.currentTimeMillis();
- gamma *= 1 + state.getAmount() * TWILIGHT_ADJUSTMENT_MAX_GAMMA;
+ final long duration = state.sunriseTimeMillis() - state.sunsetTimeMillis();
+ final long progress = System.currentTimeMillis() - state.sunsetTimeMillis();
+ final float amount = (float) Math.pow(2.0 * progress / duration - 1.0, 2.0);
+ gamma *= 1 + amount * TWILIGHT_ADJUSTMENT_MAX_GAMMA;
if (DEBUG) {
- Slog.d(TAG, "updateAutoBrightness: twilight amount=" + state.getAmount());
+ Slog.d(TAG, "updateAutoBrightness: twilight amount=" + amount);
}
}
}
@@ -621,7 +624,7 @@
private final TwilightListener mTwilightListener = new TwilightListener() {
@Override
- public void onTwilightStateChanged() {
+ public void onTwilightStateChanged(@Nullable TwilightState state) {
updateAutoBrightness(true /*sendUpdate*/);
}
};
diff --git a/services/core/java/com/android/server/display/DisplayAdapter.java b/services/core/java/com/android/server/display/DisplayAdapter.java
index 701b9f1..6ba25a5 100644
--- a/services/core/java/com/android/server/display/DisplayAdapter.java
+++ b/services/core/java/com/android/server/display/DisplayAdapter.java
@@ -49,13 +49,6 @@
*/
private static final AtomicInteger NEXT_DISPLAY_MODE_ID = new AtomicInteger(1); // 0 = no mode.
- /**
- * Used to generate globally unique color transform ids.
- *
- * Valid IDs start at 1 with 0 as the sentinel value for the default mode.
- */
- private static final AtomicInteger NEXT_COLOR_TRANSFORM_ID = new AtomicInteger(1);
-
// Called with SyncRoot lock held.
public DisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
Context context, Handler handler, Listener listener, String name) {
@@ -141,11 +134,6 @@
NEXT_DISPLAY_MODE_ID.getAndIncrement(), width, height, refreshRate);
}
- public static Display.ColorTransform createColorTransform(int colorTransform) {
- return new Display.ColorTransform(
- NEXT_COLOR_TRANSFORM_ID.getAndIncrement(), colorTransform);
- }
-
public interface Listener {
public void onDisplayDeviceEvent(DisplayDevice device, int event);
public void onTraversalRequested();
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 7af0bdb..839ab4d 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -94,6 +94,11 @@
}
/**
+ * Returns whether the unique id of the device is stable across reboots.
+ */
+ public abstract boolean hasStableUniqueId();
+
+ /**
* Gets information about the display device.
*
* The information returned should not change between calls unless the display
@@ -135,7 +140,7 @@
/**
* Sets the mode, if supported.
*/
- public void requestColorTransformAndModeInTransactionLocked(int colorTransformId, int modeId) {
+ public void requestDisplayModesInTransactionLocked(int colorMode, int modeId) {
}
/**
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 5ce66fa..6719182 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -118,6 +118,11 @@
public static final int DIFF_OTHER = 1 << 1;
/**
+ * Diff result: The color mode fields differ.
+ */
+ public static final int DIFF_COLOR_MODE = 1 << 2;
+
+ /**
* Gets the name of the display device, which may be derived from EDID or
* other sources. The name may be localized and displayed to the user.
*/
@@ -155,14 +160,11 @@
*/
public Display.Mode[] supportedModes = Display.Mode.EMPTY_ARRAY;
- /** The active color transform of the display */
- public int colorTransformId;
+ /** The active color mode of the display */
+ public int colorMode;
- /** The default color transform of the display */
- public int defaultColorTransformId;
-
- /** The supported color transforms of the display */
- public Display.ColorTransform[] supportedColorTransforms = Display.ColorTransform.EMPTY_ARRAY;
+ /** The supported color modes of the display */
+ public int[] supportedColorModes = { Display.COLOR_MODE_DEFAULT };
/**
* The HDR capabilities this display claims to support.
@@ -283,6 +285,9 @@
if (state != other.state) {
diff |= DIFF_STATE;
}
+ if (colorMode != other.colorMode) {
+ diff |= DIFF_COLOR_MODE;
+ }
if (!Objects.equal(name, other.name)
|| !Objects.equal(uniqueId, other.uniqueId)
|| width != other.width
@@ -290,9 +295,7 @@
|| modeId != other.modeId
|| defaultModeId != other.defaultModeId
|| !Arrays.equals(supportedModes, other.supportedModes)
- || colorTransformId != other.colorTransformId
- || defaultColorTransformId != other.defaultColorTransformId
- || !Arrays.equals(supportedColorTransforms, other.supportedColorTransforms)
+ || !Arrays.equals(supportedColorModes, other.supportedColorModes)
|| !Objects.equal(hdrCapabilities, other.hdrCapabilities)
|| densityDpi != other.densityDpi
|| xDpi != other.xDpi
@@ -324,9 +327,8 @@
modeId = other.modeId;
defaultModeId = other.defaultModeId;
supportedModes = other.supportedModes;
- colorTransformId = other.colorTransformId;
- defaultColorTransformId = other.defaultColorTransformId;
- supportedColorTransforms = other.supportedColorTransforms;
+ colorMode = other.colorMode;
+ supportedColorModes = other.supportedColorModes;
hdrCapabilities = other.hdrCapabilities;
densityDpi = other.densityDpi;
xDpi = other.xDpi;
@@ -353,9 +355,8 @@
sb.append(", modeId ").append(modeId);
sb.append(", defaultModeId ").append(defaultModeId);
sb.append(", supportedModes ").append(Arrays.toString(supportedModes));
- sb.append(", colorTransformId ").append(colorTransformId);
- sb.append(", defaultColorTransformId ").append(defaultColorTransformId);
- sb.append(", supportedColorTransforms ").append(Arrays.toString(supportedColorTransforms));
+ sb.append(", colorMode ").append(colorMode);
+ sb.append(", supportedColorModes ").append(Arrays.toString(supportedColorModes));
sb.append(", HdrCapabilities ").append(hdrCapabilities);
sb.append(", density ").append(densityDpi);
sb.append(", ").append(xDpi).append(" x ").append(yDpi).append(" dpi");
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 6a6570b..0abd2e7 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -239,11 +239,17 @@
@Override
public void onStart() {
+ // We need to pre-load the persistent data store so it's ready before the default display
+ // adapter is up so that we have it's configuration. We could load it lazily, but since
+ // we're going to have to read it in eventually we may as well do it here rather than after
+ // we've waited for the diplay to register itself with us.
+ mPersistentDataStore.loadIfNeeded();
mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER);
publishBinderService(Context.DISPLAY_SERVICE, new BinderService(),
true /*allowIsolated*/);
publishLocalService(DisplayManagerInternal.class, new LocalService());
+ publishLocalService(DisplayTransformManager.class, new DisplayTransformManager());
}
@Override
@@ -540,12 +546,12 @@
}
}
- private void requestColorTransformInternal(int displayId, int colorTransformId) {
+ private void requestColorModeInternal(int displayId, int colorMode) {
synchronized (mSyncRoot) {
LogicalDisplay display = mLogicalDisplays.get(displayId);
if (display != null &&
- display.getRequestedColorTransformIdLocked() != colorTransformId) {
- display.setRequestedColorTransformIdLocked(colorTransformId);
+ display.getRequestedColorModeLocked() != colorMode) {
+ display.setRequestedColorModeLocked(colorMode);
scheduleTraversalLocked(false);
}
}
@@ -690,11 +696,15 @@
device.mDebugLastLoggedDeviceInfo = info;
mDisplayDevices.add(device);
- addLogicalDisplayLocked(device);
+ LogicalDisplay display = addLogicalDisplayLocked(device);
Runnable work = updateDisplayStateLocked(device);
if (work != null) {
work.run();
}
+ if (display != null && display.getPrimaryDisplayDeviceLocked() == device) {
+ int colorMode = mPersistentDataStore.getColorMode(device);
+ display.setRequestedColorModeLocked(colorMode);
+ }
scheduleTraversalLocked(false);
}
@@ -713,6 +723,13 @@
} else if (diff != 0) {
Slog.i(TAG, "Display device changed: " + info);
}
+ if ((diff & DisplayDeviceInfo.DIFF_COLOR_MODE) != 0) {
+ try {
+ mPersistentDataStore.setColorMode(device, info.colorMode);
+ } finally {
+ mPersistentDataStore.saveIfNeeded();
+ }
+ }
device.mDebugLastLoggedDeviceInfo = info;
device.applyPendingDisplayDeviceInfoChangesLocked();
@@ -765,7 +782,7 @@
// Adds a new logical display based on the given display device.
// Sends notifications if needed.
- private void addLogicalDisplayLocked(DisplayDevice device) {
+ private LogicalDisplay addLogicalDisplayLocked(DisplayDevice device) {
DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
boolean isDefault = (deviceInfo.flags
& DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0;
@@ -777,7 +794,7 @@
if (!isDefault && mSingleDisplayDemoMode) {
Slog.i(TAG, "Not creating a logical display for a secondary display "
+ " because single display demo mode is enabled: " + deviceInfo);
- return;
+ return null;
}
final int displayId = assignDisplayIdLocked(isDefault);
@@ -789,7 +806,7 @@
// This should never happen currently.
Slog.w(TAG, "Ignoring display device because the logical display "
+ "created from it was not considered valid: " + deviceInfo);
- return;
+ return null;
}
mLogicalDisplays.put(displayId, display);
@@ -800,6 +817,7 @@
}
sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
+ return display;
}
private int assignDisplayIdLocked(boolean isDefault) {
@@ -1067,6 +1085,9 @@
if (mDisplayPowerController != null) {
mDisplayPowerController.dump(pw);
}
+
+ pw.println();
+ mPersistentDataStore.dump(pw);
}
}
@@ -1351,13 +1372,13 @@
}
@Override // Binder call
- public void requestColorTransform(int displayId, int colorTransformId) {
+ public void requestColorMode(int displayId, int colorMode) {
mContext.enforceCallingOrSelfPermission(
- Manifest.permission.CONFIGURE_DISPLAY_COLOR_TRANSFORM,
- "Permission required to change the display color transform");
+ Manifest.permission.CONFIGURE_DISPLAY_COLOR_MODE,
+ "Permission required to change the display color mode");
final long token = Binder.clearCallingIdentity();
try {
- requestColorTransformInternal(displayId, colorTransformId);
+ requestColorModeInternal(displayId, colorMode);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 8f8afd5..61af8ed 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1181,6 +1181,10 @@
}
private static Spline createAutoBrightnessSpline(int[] lux, int[] brightness) {
+ if (lux == null || lux.length == 0 || brightness == null || brightness.length == 0) {
+ Slog.e(TAG, "Could not create auto-brightness spline.");
+ return null;
+ }
try {
final int n = brightness.length;
float[] x = new float[n];
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
new file mode 100644
index 0000000..6902b1a
--- /dev/null
+++ b/services/core/java/com/android/server/display/DisplayTransformManager.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.opengl.Matrix;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Arrays;
+
+/**
+ * Manager for applying color transformations to the display.
+ */
+public class DisplayTransformManager {
+
+ private static final String TAG = "DisplayTransformManager";
+
+ /**
+ * Color transform level used by Night display to tint the display red.
+ */
+ public static final int LEVEL_COLOR_MATRIX_NIGHT_DISPLAY = 100;
+ /**
+ * Color transform level used by A11y services to make the display monochromatic.
+ */
+ public static final int LEVEL_COLOR_MATRIX_GRAYSCALE = 200;
+ /**
+ * Color transform level used by A11y services to invert the display colors.
+ */
+ public static final int LEVEL_COLOR_MATRIX_INVERT_COLOR = 300;
+
+ /**
+ * Map of level -> color transformation matrix.
+ */
+ @GuardedBy("mColorMatrix")
+ private final SparseArray<float[]> mColorMatrix = new SparseArray<>(3);
+ /**
+ * Temporary matrix used internally by {@link #computeColorMatrixLocked()}.
+ */
+ @GuardedBy("mColorMatrix")
+ private final float[][] mTempColorMatrix = new float[2][16];
+
+ /**
+ * Lock used for synchronize access to {@link #mDaltonizerMode}.
+ */
+ private final Object mDaltonizerModeLock = new Object();
+ @GuardedBy("mDaltonizerModeLock")
+ private int mDaltonizerMode = -1;
+
+ /* package */ DisplayTransformManager() {
+ }
+
+ /**
+ * Returns a copy of the color transform matrix set for a given level.
+ */
+ public float[] getColorMatrix(int key) {
+ synchronized (mColorMatrix) {
+ final float[] value = mColorMatrix.get(key);
+ return value == null ? null : Arrays.copyOf(value, value.length);
+ }
+ }
+
+ /**
+ * Sets and applies a current color transform matrix for a given level.
+ * <p>
+ * Note: all color transforms are first composed to a single matrix in ascending order based
+ * on level before being applied to the display.
+ *
+ * @param level the level used to identify and compose the color transform (low -> high)
+ * @param value the 4x4 color transform matrix (in column-major order), or {@code null} to
+ * remove the color transform matrix associated with the provided level
+ */
+ public void setColorMatrix(int level, float[] value) {
+ if (value != null && value.length != 16) {
+ throw new IllegalArgumentException("Expected length: 16 (4x4 matrix)"
+ + ", actual length: " + value.length);
+ }
+
+ synchronized (mColorMatrix) {
+ final float[] oldValue = mColorMatrix.get(level);
+ if (!Arrays.equals(oldValue, value)) {
+ if (value == null) {
+ mColorMatrix.remove(level);
+ } else if (oldValue == null) {
+ mColorMatrix.put(level, Arrays.copyOf(value, value.length));
+ } else {
+ System.arraycopy(value, 0, oldValue, 0, value.length);
+ }
+
+ // Update the current color transform.
+ applyColorMatrix(computeColorMatrixLocked());
+ }
+ }
+ }
+
+ /**
+ * Returns the composition of all current color matrices, or {@code null} if there are none.
+ */
+ @GuardedBy("mColorMatrix")
+ private float[] computeColorMatrixLocked() {
+ final int count = mColorMatrix.size();
+ if (count == 0) {
+ return null;
+ }
+
+ final float[][] result = mTempColorMatrix;
+ Matrix.setIdentityM(result[0], 0);
+ for (int i = 0; i < count; i++) {
+ float[] rhs = mColorMatrix.valueAt(i);
+ Matrix.multiplyMM(result[(i + 1) % 2], 0, result[i % 2], 0, rhs, 0);
+ }
+ return result[count % 2];
+ }
+
+ /**
+ * Returns the current Daltonization mode.
+ */
+ public int getDaltonizerMode() {
+ synchronized (mDaltonizerModeLock) {
+ return mDaltonizerMode;
+ }
+ }
+
+ /**
+ * Sets the current Daltonization mode. This adjusts the color space to correct for or simulate
+ * various types of color blindness.
+ *
+ * @param mode the new Daltonization mode, or -1 to disable
+ */
+ public void setDaltonizerMode(int mode) {
+ synchronized (mDaltonizerModeLock) {
+ if (mDaltonizerMode != mode) {
+ mDaltonizerMode = mode;
+ applyDaltonizerMode(mode);
+ }
+ }
+ }
+
+ /**
+ * Propagates the provided color transformation matrix to the SurfaceFlinger.
+ */
+ private static void applyColorMatrix(float[] m) {
+ final IBinder flinger = ServiceManager.getService("SurfaceFlinger");
+ if (flinger != null) {
+ final Parcel data = Parcel.obtain();
+ data.writeInterfaceToken("android.ui.ISurfaceComposer");
+ if (m != null) {
+ data.writeInt(1);
+ for (int i = 0; i < 16; i++) {
+ data.writeFloat(m[i]);
+ }
+ } else {
+ data.writeInt(0);
+ }
+ try {
+ flinger.transact(1015, data, null, 0);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to set color transform", ex);
+ } finally {
+ data.recycle();
+ }
+ }
+ }
+
+ /**
+ * Propagates the provided Daltonization mode to the SurfaceFlinger.
+ */
+ private static void applyDaltonizerMode(int mode) {
+ final IBinder flinger = ServiceManager.getService("SurfaceFlinger");
+ if (flinger != null) {
+ final Parcel data = Parcel.obtain();
+ data.writeInterfaceToken("android.ui.ISurfaceComposer");
+ data.writeInt(mode);
+ try {
+ flinger.transact(1014, data, null, 0);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to set Daltonizer mode", ex);
+ } finally {
+ data.recycle();
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 7b16ea6..61c2eac 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -39,6 +39,8 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
/**
* A display adapter for the local displays managed by Surface Flinger.
@@ -100,14 +102,25 @@
builtInDisplayId);
return;
}
+ int activeColorMode = SurfaceControl.getActiveColorMode(displayToken);
+ if (activeColorMode < 0) {
+ // We failed to get the active color mode. We don't bail out here since on the next
+ // configuration pass we'll go ahead and set it to whatever it was set to last (or
+ // COLOR_MODE_NATIVE if this is the first configuration).
+ Slog.w(TAG, "Unable to get active color mode for display device " +
+ builtInDisplayId);
+ activeColorMode = Display.COLOR_MODE_INVALID;
+ }
+ int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken);
LocalDisplayDevice device = mDevices.get(builtInDisplayId);
if (device == null) {
// Display was added.
device = new LocalDisplayDevice(displayToken, builtInDisplayId,
- configs, activeConfig);
+ configs, activeConfig, colorModes, activeColorMode);
mDevices.put(builtInDisplayId, device);
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
- } else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig)) {
+ } else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig,
+ colorModes, activeColorMode)) {
// Display properties changed.
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
}
@@ -144,8 +157,7 @@
private final int mBuiltInDisplayId;
private final Light mBacklight;
private final SparseArray<DisplayModeRecord> mSupportedModes = new SparseArray<>();
- private final SparseArray<Display.ColorTransform> mSupportedColorTransforms =
- new SparseArray<>();
+ private final ArrayList<Integer> mSupportedColorModes = new ArrayList<>();
private DisplayDeviceInfo mInfo;
private boolean mHavePendingChanges;
@@ -155,18 +167,20 @@
private int mDefaultModeId;
private int mActiveModeId;
private boolean mActiveModeInvalid;
- private int mDefaultColorTransformId;
- private int mActiveColorTransformId;
- private boolean mActiveColorTransformInvalid;
+ private int mActiveColorMode;
+ private boolean mActiveColorModeInvalid;
private Display.HdrCapabilities mHdrCapabilities;
private SurfaceControl.PhysicalDisplayInfo mDisplayInfos[];
public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId,
- SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) {
+ SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo,
+ int[] colorModes, int activeColorMode) {
super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + builtInDisplayId);
mBuiltInDisplayId = builtInDisplayId;
- updatePhysicalDisplayInfoLocked(physicalDisplayInfos, activeDisplayInfo);
+ updatePhysicalDisplayInfoLocked(physicalDisplayInfos, activeDisplayInfo,
+ colorModes, activeColorMode);
+ updateColorModesLocked(colorModes, activeColorMode);
if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
LightsManager lights = LocalServices.getService(LightsManager.class);
mBacklight = lights.getLight(LightsManager.LIGHT_ID_BACKLIGHT);
@@ -176,39 +190,16 @@
mHdrCapabilities = SurfaceControl.getHdrCapabilities(displayToken);
}
+ @Override
+ public boolean hasStableUniqueId() {
+ return true;
+ }
+
public boolean updatePhysicalDisplayInfoLocked(
- SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) {
+ SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo,
+ int[] colorModes, int activeColorMode) {
mDisplayInfos = Arrays.copyOf(physicalDisplayInfos, physicalDisplayInfos.length);
mActivePhysIndex = activeDisplayInfo;
- ArrayList<Display.ColorTransform> colorTransforms = new ArrayList<>();
-
- // Build an updated list of all existing color transforms.
- boolean colorTransformsAdded = false;
- Display.ColorTransform activeColorTransform = null;
- for (int i = 0; i < physicalDisplayInfos.length; i++) {
- SurfaceControl.PhysicalDisplayInfo info = physicalDisplayInfos[i];
- // First check to see if we've already added this color transform
- boolean existingMode = false;
- for (int j = 0; j < colorTransforms.size(); j++) {
- if (colorTransforms.get(j).getColorTransform() == info.colorTransform) {
- existingMode = true;
- break;
- }
- }
- if (existingMode) {
- continue;
- }
- Display.ColorTransform colorTransform = findColorTransform(info);
- if (colorTransform == null) {
- colorTransform = createColorTransform(info.colorTransform);
- colorTransformsAdded = true;
- }
- colorTransforms.add(colorTransform);
- if (i == activeDisplayInfo) {
- activeColorTransform = colorTransform;
- }
- }
-
// Build an updated list of all existing modes.
ArrayList<DisplayModeRecord> records = new ArrayList<DisplayModeRecord>();
boolean modesAdded = false;
@@ -254,21 +245,10 @@
mActiveModeInvalid = true;
sendTraversalRequestLocked();
}
- // Check whether surface flinger spontaneously changed color transforms out from under
- // us.
- if (mActiveColorTransformId != 0
- && mActiveColorTransformId != activeColorTransform.getId()) {
- mActiveColorTransformInvalid = true;
- sendTraversalRequestLocked();
- }
- boolean colorTransformsChanged =
- colorTransforms.size() != mSupportedColorTransforms.size()
- || colorTransformsAdded;
boolean recordsChanged = records.size() != mSupportedModes.size() || modesAdded;
- // If neither the records nor the supported color transforms have changed then we're
- // done here.
- if (!recordsChanged && !colorTransformsChanged) {
+ // If the records haven't changed then we're done here.
+ if (!recordsChanged) {
return false;
}
// Update the index of modes.
@@ -278,24 +258,13 @@
for (DisplayModeRecord record : records) {
mSupportedModes.put(record.mMode.getModeId(), record);
}
- mSupportedColorTransforms.clear();
- for (Display.ColorTransform colorTransform : colorTransforms) {
- mSupportedColorTransforms.put(colorTransform.getId(), colorTransform);
- }
-
- // Update the default mode and color transform if needed. This needs to be done in
- // tandem so we always have a default state to fall back to.
- if (findDisplayInfoIndexLocked(mDefaultColorTransformId, mDefaultModeId) < 0) {
+ // Update the default mode, if needed.
+ if (findDisplayInfoIndexLocked(mDefaultModeId) < 0) {
if (mDefaultModeId != 0) {
Slog.w(TAG, "Default display mode no longer available, using currently"
+ " active mode as default.");
}
mDefaultModeId = activeRecord.mMode.getModeId();
- if (mDefaultColorTransformId != 0) {
- Slog.w(TAG, "Default color transform no longer available, using currently"
- + " active color transform as default");
- }
- mDefaultColorTransformId = activeColorTransform.getId();
}
// Determine whether the active mode is still there.
if (mSupportedModes.indexOfKey(mActiveModeId) < 0) {
@@ -307,20 +276,62 @@
mActiveModeInvalid = true;
}
- // Determine whether the active color transform is still there.
- if (mSupportedColorTransforms.indexOfKey(mActiveColorTransformId) < 0) {
- if (mActiveColorTransformId != 0) {
- Slog.w(TAG, "Active color transform no longer available, reverting"
- + " to default transform.");
- }
- mActiveColorTransformId = mDefaultColorTransformId;
- mActiveColorTransformInvalid = true;
- }
// Schedule traversals so that we apply pending changes.
sendTraversalRequestLocked();
return true;
}
+ private boolean updateColorModesLocked(int[] colorModes,
+ int activeColorMode) {
+ List<Integer> pendingColorModes = new ArrayList<>();
+
+ // Build an updated list of all existing color modes.
+ boolean colorModesAdded = false;
+ for (int colorMode: colorModes) {
+ if (!mSupportedColorModes.contains(colorMode)) {
+ colorModesAdded = true;
+ }
+ pendingColorModes.add(colorMode);
+ }
+
+ boolean colorModesChanged =
+ pendingColorModes.size() != mSupportedColorModes.size()
+ || colorModesAdded;
+
+ // If the supported color modes haven't changed then we're done here.
+ if (!colorModesChanged) {
+ return false;
+ }
+
+ mHavePendingChanges = true;
+
+ mSupportedColorModes.clear();
+ mSupportedColorModes.addAll(pendingColorModes);
+ Collections.sort(mSupportedColorModes);
+
+ // Determine whether the active color mode is still there.
+ if (!mSupportedColorModes.contains(mActiveColorMode)) {
+ if (mActiveColorMode != 0) {
+ Slog.w(TAG, "Active color mode no longer available, reverting"
+ + " to default mode.");
+ mActiveColorMode = Display.COLOR_MODE_DEFAULT;
+ mActiveColorModeInvalid = true;
+ } else {
+ if (!mSupportedColorModes.isEmpty()) {
+ // This should never happen.
+ Slog.e(TAG, "Default and active color mode is no longer available!"
+ + " Reverting to first available mode.");
+ mActiveColorMode = mSupportedColorModes.get(0);
+ mActiveColorModeInvalid = true;
+ } else {
+ // This should really never happen.
+ Slog.e(TAG, "No color modes available!");
+ }
+ }
+ }
+ return true;
+ }
+
private DisplayModeRecord findDisplayModeRecord(SurfaceControl.PhysicalDisplayInfo info) {
for (int i = 0; i < mSupportedModes.size(); i++) {
DisplayModeRecord record = mSupportedModes.valueAt(i);
@@ -331,16 +342,6 @@
return null;
}
- private Display.ColorTransform findColorTransform(SurfaceControl.PhysicalDisplayInfo info) {
- for (int i = 0; i < mSupportedColorTransforms.size(); i++) {
- Display.ColorTransform transform = mSupportedColorTransforms.valueAt(i);
- if (transform.getColorTransform() == info.colorTransform) {
- return transform;
- }
- }
- return null;
- }
-
@Override
public void applyPendingDisplayDeviceInfoChangesLocked() {
if (mHavePendingChanges) {
@@ -363,12 +364,11 @@
DisplayModeRecord record = mSupportedModes.valueAt(i);
mInfo.supportedModes[i] = record.mMode;
}
- mInfo.colorTransformId = mActiveColorTransformId;
- mInfo.defaultColorTransformId = mDefaultColorTransformId;
- mInfo.supportedColorTransforms =
- new Display.ColorTransform[mSupportedColorTransforms.size()];
- for (int i = 0; i < mSupportedColorTransforms.size(); i++) {
- mInfo.supportedColorTransforms[i] = mSupportedColorTransforms.valueAt(i);
+ mInfo.colorMode = mActiveColorMode;
+ mInfo.supportedColorModes =
+ new int[mSupportedColorModes.size()];
+ for (int i = 0; i < mSupportedColorModes.size(); i++) {
+ mInfo.supportedColorModes[i] = mSupportedColorModes.get(i);
}
mInfo.hdrCapabilities = mHdrCapabilities;
mInfo.appVsyncOffsetNanos = phys.appVsyncOffsetNanos;
@@ -520,8 +520,15 @@
}
@Override
- public void requestColorTransformAndModeInTransactionLocked(
- int colorTransformId, int modeId) {
+ public void requestDisplayModesInTransactionLocked(
+ int colorMode, int modeId) {
+ if (requestModeInTransactionLocked(modeId) ||
+ requestColorModeInTransactionLocked(colorMode)) {
+ updateDeviceInfoLocked();
+ }
+ }
+
+ public boolean requestModeInTransactionLocked(int modeId) {
if (modeId == 0) {
modeId = mDefaultModeId;
} else if (mSupportedModes.indexOfKey(modeId) < 0) {
@@ -530,37 +537,36 @@
modeId = mDefaultModeId;
}
- if (colorTransformId == 0) {
- colorTransformId = mDefaultColorTransformId;
- } else if (mSupportedColorTransforms.indexOfKey(colorTransformId) < 0) {
- Slog.w(TAG, "Requested color transform " + colorTransformId + " is not supported"
- + " by this display, reverting to the default color transform");
- colorTransformId = mDefaultColorTransformId;
- }
- int physIndex = findDisplayInfoIndexLocked(colorTransformId, modeId);
+ int physIndex = findDisplayInfoIndexLocked(modeId);
if (physIndex < 0) {
- Slog.w(TAG, "Requested color transform, mode ID pair (" + colorTransformId + ", "
- + modeId + ") not available, trying color transform with default mode ID");
+ Slog.w(TAG, "Requested mode ID " + modeId + " not available,"
+ + " trying with default mode ID");
modeId = mDefaultModeId;
- physIndex = findDisplayInfoIndexLocked(colorTransformId, modeId);
- if (physIndex < 0) {
- Slog.w(TAG, "Requested color transform with default mode ID still not"
- + " available, falling back to default color transform with default"
- + " mode.");
- colorTransformId = mDefaultColorTransformId;
- physIndex = findDisplayInfoIndexLocked(colorTransformId, modeId);
- }
+ physIndex = findDisplayInfoIndexLocked(modeId);
}
if (mActivePhysIndex == physIndex) {
- return;
+ return false;
}
SurfaceControl.setActiveConfig(getDisplayTokenLocked(), physIndex);
mActivePhysIndex = physIndex;
mActiveModeId = modeId;
mActiveModeInvalid = false;
- mActiveColorTransformId = colorTransformId;
- mActiveColorTransformInvalid = false;
- updateDeviceInfoLocked();
+ return true;
+ }
+
+ public boolean requestColorModeInTransactionLocked(int colorMode) {
+ if (mActiveColorMode == colorMode) {
+ return false;
+ }
+ if (!mSupportedColorModes.contains(colorMode)) {
+ Slog.w(TAG, "Unable to find color mode " + colorMode
+ + ", ignoring request.");
+ return false;
+ }
+ SurfaceControl.setActiveColorMode(getDisplayTokenLocked(), colorMode);
+ mActiveColorMode = colorMode;
+ mActiveColorModeInvalid = false;
+ return true;
}
@Override
@@ -569,7 +575,7 @@
pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId);
pw.println("mActivePhysIndex=" + mActivePhysIndex);
pw.println("mActiveModeId=" + mActiveModeId);
- pw.println("mActiveColorTransformId=" + mActiveColorTransformId);
+ pw.println("mActiveColorMode=" + mActiveColorMode);
pw.println("mState=" + Display.stateToString(mState));
pw.println("mBrightness=" + mBrightness);
pw.println("mBacklight=" + mBacklight);
@@ -581,24 +587,22 @@
for (int i = 0; i < mSupportedModes.size(); i++) {
pw.println(" " + mSupportedModes.valueAt(i));
}
- pw.println("mSupportedColorTransforms=[");
- for (int i = 0; i < mSupportedColorTransforms.size(); i++) {
+ pw.print("mSupportedColorModes=[");
+ for (int i = 0; i < mSupportedColorModes.size(); i++) {
if (i != 0) {
pw.print(", ");
}
- pw.print(mSupportedColorTransforms.valueAt(i));
+ pw.print(mSupportedColorModes.get(i));
}
pw.println("]");
}
- private int findDisplayInfoIndexLocked(int colorTransformId, int modeId) {
+ private int findDisplayInfoIndexLocked(int modeId) {
DisplayModeRecord record = mSupportedModes.get(modeId);
- Display.ColorTransform transform = mSupportedColorTransforms.get(colorTransformId);
- if (record != null && transform != null) {
+ if (record != null) {
for (int i = 0; i < mDisplayInfos.length; i++) {
SurfaceControl.PhysicalDisplayInfo info = mDisplayInfos[i];
- if (info.colorTransform == transform.getColorTransform()
- && record.hasMatchingMode(info)){
+ if (record.hasMatchingMode(info)){
return i;
}
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 973f04c..287a25a 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -74,7 +74,7 @@
private boolean mHasContent;
private int mRequestedModeId;
- private int mRequestedColorTransformId;
+ private int mRequestedColorMode;
// The display offsets to apply to the display projection.
private int mDisplayOffsetX;
@@ -236,11 +236,10 @@
mBaseDisplayInfo.defaultModeId = deviceInfo.defaultModeId;
mBaseDisplayInfo.supportedModes = Arrays.copyOf(
deviceInfo.supportedModes, deviceInfo.supportedModes.length);
- mBaseDisplayInfo.colorTransformId = deviceInfo.colorTransformId;
- mBaseDisplayInfo.defaultColorTransformId = deviceInfo.defaultColorTransformId;
- mBaseDisplayInfo.supportedColorTransforms = Arrays.copyOf(
- deviceInfo.supportedColorTransforms,
- deviceInfo.supportedColorTransforms.length);
+ mBaseDisplayInfo.colorMode = deviceInfo.colorMode;
+ mBaseDisplayInfo.supportedColorModes = Arrays.copyOf(
+ deviceInfo.supportedColorModes,
+ deviceInfo.supportedColorModes.length);
mBaseDisplayInfo.hdrCapabilities = deviceInfo.hdrCapabilities;
mBaseDisplayInfo.logicalDensityDpi = deviceInfo.densityDpi;
mBaseDisplayInfo.physicalXDpi = deviceInfo.xDpi;
@@ -282,12 +281,12 @@
// Set the layer stack.
device.setLayerStackInTransactionLocked(isBlanked ? BLANK_LAYER_STACK : mLayerStack);
- // Set the color transform and mode.
+ // Set the color mode and mode.
if (device == mPrimaryDisplayDevice) {
- device.requestColorTransformAndModeInTransactionLocked(
- mRequestedColorTransformId, mRequestedModeId);
+ device.requestDisplayModesInTransactionLocked(
+ mRequestedColorMode, mRequestedModeId);
} else {
- device.requestColorTransformAndModeInTransactionLocked(0, 0); // Revert to default.
+ device.requestDisplayModesInTransactionLocked(0, 0); // Revert to default.
}
// Only grab the display info now as it may have been changed based on the requests above.
@@ -391,15 +390,15 @@
}
/**
- * Requests the given color transform.
+ * Requests the given color mode.
*/
- public void setRequestedColorTransformIdLocked(int colorTransformId) {
- mRequestedColorTransformId = colorTransformId;
+ public void setRequestedColorModeLocked(int colorMode) {
+ mRequestedColorMode = colorMode;
}
- /** Returns the pending requested color transform. */
- public int getRequestedColorTransformIdLocked() {
- return mRequestedColorTransformId;
+ /** Returns the pending requested color mode. */
+ public int getRequestedColorModeLocked() {
+ return mRequestedColorMode;
}
/**
@@ -429,7 +428,7 @@
pw.println("mLayerStack=" + mLayerStack);
pw.println("mHasContent=" + mHasContent);
pw.println("mRequestedMode=" + mRequestedModeId);
- pw.println("mRequestedColorTransformId=" + mRequestedColorTransformId);
+ pw.println("mRequestedColorMode=" + mRequestedColorMode);
pw.println("mDisplayOffset=(" + mDisplayOffsetX + ", " + mDisplayOffsetY + ")");
pw.println("mPrimaryDisplayDevice=" + (mPrimaryDisplayDevice != null ?
mPrimaryDisplayDevice.getNameLocked() : "null"));
diff --git a/services/core/java/com/android/server/display/NightDisplayService.java b/services/core/java/com/android/server/display/NightDisplayService.java
new file mode 100644
index 0000000..07fa2ce
--- /dev/null
+++ b/services/core/java/com/android/server/display/NightDisplayService.java
@@ -0,0 +1,503 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.TypeEvaluator;
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.opengl.Matrix;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings.Secure;
+import android.util.MathUtils;
+import android.util.Slog;
+import android.view.animation.AnimationUtils;
+
+import com.android.internal.app.NightDisplayController;
+import com.android.server.SystemService;
+import com.android.server.twilight.TwilightListener;
+import com.android.server.twilight.TwilightManager;
+import com.android.server.twilight.TwilightState;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+
+import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
+
+/**
+ * Tints the display at night.
+ */
+public final class NightDisplayService extends SystemService
+ implements NightDisplayController.Callback {
+
+ private static final String TAG = "NightDisplayService";
+ private static final boolean DEBUG = false;
+
+ /**
+ * Night display ~= 3400 K.
+ */
+ private static final float[] MATRIX_NIGHT = new float[] {
+ 1, 0, 0, 0,
+ 0, 0.754f, 0, 0,
+ 0, 0, 0.516f, 0,
+ 0, 0, 0, 1
+ };
+
+ /**
+ * The identity matrix, used if one of the given matrices is {@code null}.
+ */
+ private static final float[] MATRIX_IDENTITY = new float[16];
+ static {
+ Matrix.setIdentityM(MATRIX_IDENTITY, 0);
+ }
+
+ /**
+ * Evaluator used to animate color matrix transitions.
+ */
+ private static final ColorMatrixEvaluator COLOR_MATRIX_EVALUATOR = new ColorMatrixEvaluator();
+
+ private final Handler mHandler;
+
+ private int mCurrentUser = UserHandle.USER_NULL;
+ private ContentObserver mUserSetupObserver;
+ private boolean mBootCompleted;
+
+ private NightDisplayController mController;
+ private ValueAnimator mColorMatrixAnimator;
+ private Boolean mIsActivated;
+ private AutoMode mAutoMode;
+
+ public NightDisplayService(Context context) {
+ super(context);
+ mHandler = new Handler(Looper.getMainLooper());
+ }
+
+ @Override
+ public void onStart() {
+ // Nothing to publish.
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_BOOT_COMPLETED) {
+ mBootCompleted = true;
+
+ // Register listeners now that boot is complete.
+ if (mCurrentUser != UserHandle.USER_NULL && mUserSetupObserver == null) {
+ setUp();
+ }
+ }
+ }
+
+ @Override
+ public void onStartUser(int userHandle) {
+ super.onStartUser(userHandle);
+
+ if (mCurrentUser == UserHandle.USER_NULL) {
+ onUserChanged(userHandle);
+ }
+ }
+
+ @Override
+ public void onSwitchUser(int userHandle) {
+ super.onSwitchUser(userHandle);
+
+ onUserChanged(userHandle);
+ }
+
+ @Override
+ public void onStopUser(int userHandle) {
+ super.onStopUser(userHandle);
+
+ if (mCurrentUser == userHandle) {
+ onUserChanged(UserHandle.USER_NULL);
+ }
+ }
+
+ private void onUserChanged(int userHandle) {
+ final ContentResolver cr = getContext().getContentResolver();
+
+ if (mCurrentUser != UserHandle.USER_NULL) {
+ if (mUserSetupObserver != null) {
+ cr.unregisterContentObserver(mUserSetupObserver);
+ mUserSetupObserver = null;
+ } else if (mBootCompleted) {
+ tearDown();
+ }
+ }
+
+ mCurrentUser = userHandle;
+
+ if (mCurrentUser != UserHandle.USER_NULL) {
+ if (!isUserSetupCompleted(cr, mCurrentUser)) {
+ mUserSetupObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (isUserSetupCompleted(cr, mCurrentUser)) {
+ cr.unregisterContentObserver(this);
+ mUserSetupObserver = null;
+
+ if (mBootCompleted) {
+ setUp();
+ }
+ }
+ }
+ };
+ cr.registerContentObserver(Secure.getUriFor(Secure.USER_SETUP_COMPLETE),
+ false /* notifyForDescendents */, mUserSetupObserver, mCurrentUser);
+ } else if (mBootCompleted) {
+ setUp();
+ }
+ }
+ }
+
+ private static boolean isUserSetupCompleted(ContentResolver cr, int userHandle) {
+ return Secure.getIntForUser(cr, Secure.USER_SETUP_COMPLETE, 0, userHandle) == 1;
+ }
+
+ private void setUp() {
+ // Create a new controller for the current user and start listening for changes.
+ mController = new NightDisplayController(getContext(), mCurrentUser);
+ mController.setListener(this);
+
+ // Initialize the current auto mode.
+ onAutoModeChanged(mController.getAutoMode());
+
+ // Force the initialization current activated state.
+ if (mIsActivated == null) {
+ onActivated(mController.isActivated());
+ }
+ }
+
+ private void tearDown() {
+ if (mController != null) {
+ mController.setListener(null);
+ mController = null;
+ }
+
+ if (mAutoMode != null) {
+ mAutoMode.onStop();
+ mAutoMode = null;
+ }
+
+ if (mColorMatrixAnimator != null) {
+ mColorMatrixAnimator.end();
+ mColorMatrixAnimator = null;
+ }
+
+ mIsActivated = null;
+ }
+
+ @Override
+ public void onActivated(boolean activated) {
+ if (mIsActivated == null || mIsActivated != activated) {
+ Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
+
+ if (mAutoMode != null) {
+ mAutoMode.onActivated(activated);
+ }
+
+ mIsActivated = activated;
+
+ // Cancel the old animator if still running.
+ if (mColorMatrixAnimator != null) {
+ mColorMatrixAnimator.cancel();
+ }
+
+ final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
+ final float[] from = dtm.getColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY);
+ final float[] to = mIsActivated ? MATRIX_NIGHT : null;
+
+ mColorMatrixAnimator = ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR,
+ from == null ? MATRIX_IDENTITY : from, to == null ? MATRIX_IDENTITY : to);
+ mColorMatrixAnimator.setDuration(getContext().getResources()
+ .getInteger(android.R.integer.config_longAnimTime));
+ mColorMatrixAnimator.setInterpolator(AnimationUtils.loadInterpolator(
+ getContext(), android.R.interpolator.fast_out_slow_in));
+ mColorMatrixAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animator) {
+ final float[] value = (float[]) animator.getAnimatedValue();
+ dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, value);
+ }
+ });
+ mColorMatrixAnimator.addListener(new AnimatorListenerAdapter() {
+
+ private boolean mIsCancelled;
+
+ @Override
+ public void onAnimationCancel(Animator animator) {
+ mIsCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ if (!mIsCancelled) {
+ // Ensure final color matrix is set at the end of the animation. If the
+ // animation is cancelled then don't set the final color matrix so the new
+ // animator can pick up from where this one left off.
+ dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, to);
+ }
+ mColorMatrixAnimator = null;
+ }
+ });
+ mColorMatrixAnimator.start();
+ }
+ }
+
+ @Override
+ public void onAutoModeChanged(int autoMode) {
+ if (mAutoMode != null) {
+ mAutoMode.onStop();
+ mAutoMode = null;
+ }
+
+ if (autoMode == NightDisplayController.AUTO_MODE_CUSTOM) {
+ mAutoMode = new CustomAutoMode();
+ } else if (autoMode == NightDisplayController.AUTO_MODE_TWILIGHT) {
+ mAutoMode = new TwilightAutoMode();
+ }
+
+ if (mAutoMode != null) {
+ mAutoMode.onStart();
+ }
+ }
+
+ @Override
+ public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) {
+ if (mAutoMode != null) {
+ mAutoMode.onCustomStartTimeChanged(startTime);
+ }
+ }
+
+ @Override
+ public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) {
+ if (mAutoMode != null) {
+ mAutoMode.onCustomEndTimeChanged(endTime);
+ }
+ }
+
+ private abstract class AutoMode implements NightDisplayController.Callback {
+ public abstract void onStart();
+ public abstract void onStop();
+ }
+
+ private class CustomAutoMode extends AutoMode implements AlarmManager.OnAlarmListener {
+
+ private final AlarmManager mAlarmManager;
+ private final BroadcastReceiver mTimeChangedReceiver;
+
+ private NightDisplayController.LocalTime mStartTime;
+ private NightDisplayController.LocalTime mEndTime;
+
+ private Calendar mLastActivatedTime;
+
+ public CustomAutoMode() {
+ mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
+ mTimeChangedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ updateActivated();
+ }
+ };
+ }
+
+ private void updateActivated() {
+ final Calendar now = Calendar.getInstance();
+ final Calendar startTime = mStartTime.getDateTimeBefore(now);
+ final Calendar endTime = mEndTime.getDateTimeAfter(startTime);
+ final boolean activated = now.before(endTime);
+
+ boolean setActivated = mIsActivated == null || mLastActivatedTime == null;
+ if (!setActivated && mIsActivated != activated) {
+ final TimeZone currentTimeZone = now.getTimeZone();
+ if (!currentTimeZone.equals(mLastActivatedTime.getTimeZone())) {
+ final int year = mLastActivatedTime.get(Calendar.YEAR);
+ final int dayOfYear = mLastActivatedTime.get(Calendar.DAY_OF_YEAR);
+ final int hourOfDay = mLastActivatedTime.get(Calendar.HOUR_OF_DAY);
+ final int minute = mLastActivatedTime.get(Calendar.MINUTE);
+
+ mLastActivatedTime.setTimeZone(currentTimeZone);
+ mLastActivatedTime.set(Calendar.YEAR, year);
+ mLastActivatedTime.set(Calendar.DAY_OF_YEAR, dayOfYear);
+ mLastActivatedTime.set(Calendar.HOUR_OF_DAY, hourOfDay);
+ mLastActivatedTime.set(Calendar.MINUTE, minute);
+ }
+
+ if (mIsActivated) {
+ setActivated = now.before(mStartTime.getDateTimeBefore(mLastActivatedTime))
+ || now.after(mEndTime.getDateTimeAfter(mLastActivatedTime));
+ } else {
+ setActivated = now.before(mEndTime.getDateTimeBefore(mLastActivatedTime))
+ || now.after(mStartTime.getDateTimeAfter(mLastActivatedTime));
+ }
+ }
+
+ if (setActivated) {
+ mController.setActivated(activated);
+ }
+ updateNextAlarm(mIsActivated, now);
+ }
+
+ private void updateNextAlarm(@Nullable Boolean activated, @NonNull Calendar now) {
+ if (activated != null) {
+ final Calendar next = activated ? mEndTime.getDateTimeAfter(now)
+ : mStartTime.getDateTimeAfter(now);
+ mAlarmManager.setExact(AlarmManager.RTC, next.getTimeInMillis(), TAG, this, null);
+ }
+ }
+
+ @Override
+ public void onStart() {
+ final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED);
+ intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
+
+ mStartTime = mController.getCustomStartTime();
+ mEndTime = mController.getCustomEndTime();
+
+ // Force an update to initialize state.
+ updateActivated();
+ }
+
+ @Override
+ public void onStop() {
+ getContext().unregisterReceiver(mTimeChangedReceiver);
+
+ mAlarmManager.cancel(this);
+ mLastActivatedTime = null;
+ }
+
+ @Override
+ public void onActivated(boolean activated) {
+ final Calendar now = Calendar.getInstance();
+ if (mIsActivated != null) {
+ mLastActivatedTime = now;
+ }
+ updateNextAlarm(activated, now);
+ }
+
+ @Override
+ public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) {
+ mStartTime = startTime;
+ mLastActivatedTime = null;
+ updateActivated();
+ }
+
+ @Override
+ public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) {
+ mEndTime = endTime;
+ mLastActivatedTime = null;
+ updateActivated();
+ }
+
+ @Override
+ public void onAlarm() {
+ if (DEBUG) Slog.d(TAG, "onAlarm");
+ updateActivated();
+ }
+ }
+
+ private class TwilightAutoMode extends AutoMode implements TwilightListener {
+
+ private final TwilightManager mTwilightManager;
+
+ private Calendar mLastActivatedTime;
+
+ public TwilightAutoMode() {
+ mTwilightManager = getLocalService(TwilightManager.class);
+ }
+
+ private void updateActivated(TwilightState state) {
+ final boolean isNight = state != null && state.isNight();
+ boolean setActivated = mIsActivated == null || mIsActivated != isNight;
+ if (setActivated && state != null && mLastActivatedTime != null) {
+ final Calendar sunrise = state.sunrise();
+ final Calendar sunset = state.sunset();
+ if (sunrise.before(sunset)) {
+ setActivated = mLastActivatedTime.before(sunrise)
+ || mLastActivatedTime.after(sunset);
+ } else {
+ setActivated = mLastActivatedTime.before(sunset)
+ || mLastActivatedTime.after(sunrise);
+ }
+ }
+
+ if (setActivated) {
+ mController.setActivated(isNight);
+ }
+ }
+
+ @Override
+ public void onStart() {
+ mTwilightManager.registerListener(this, mHandler);
+
+ // Force an update to initialize state.
+ updateActivated(mTwilightManager.getLastTwilightState());
+ }
+
+ @Override
+ public void onStop() {
+ mTwilightManager.unregisterListener(this);
+ mLastActivatedTime = null;
+ }
+
+ @Override
+ public void onActivated(boolean activated) {
+ if (mIsActivated != null) {
+ mLastActivatedTime = Calendar.getInstance();
+ }
+ }
+
+ @Override
+ public void onTwilightStateChanged(@Nullable TwilightState state) {
+ if (DEBUG) Slog.d(TAG, "onTwilightStateChanged");
+ updateActivated(state);
+ }
+ }
+
+ /**
+ * Interpolates between two 4x4 color transform matrices (in column-major order).
+ */
+ private static class ColorMatrixEvaluator implements TypeEvaluator<float[]> {
+
+ /**
+ * Result matrix returned by {@link #evaluate(float, float[], float[])}.
+ */
+ private final float[] mResultMatrix = new float[16];
+
+ @Override
+ public float[] evaluate(float fraction, float[] startValue, float[] endValue) {
+ for (int i = 0; i < mResultMatrix.length; i++) {
+ mResultMatrix[i] = MathUtils.lerp(startValue[i], endValue[i], fraction);
+ }
+ return mResultMatrix;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index cf6264a..27327d4 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -266,6 +266,11 @@
}
@Override
+ public boolean hasStableUniqueId() {
+ return false;
+ }
+
+ @Override
public void performTraversalInTransactionLocked() {
if (mSurfaceTexture != null) {
if (mSurface == null) {
@@ -310,7 +315,7 @@
}
@Override
- public void requestColorTransformAndModeInTransactionLocked(int color, int id) {
+ public void requestDisplayModesInTransactionLocked(int color, int id) {
int index = -1;
if (id == 0) {
// Use the default.
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index d676b35..5616fb9 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -27,6 +27,7 @@
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
+import android.view.Display;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -35,8 +36,11 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
import libcore.io.IoUtils;
import libcore.util.Objects;
@@ -50,8 +54,13 @@
* <display-manager-state>
* <remembered-wifi-displays>
* <wifi-display deviceAddress="00:00:00:00:00:00" deviceName="XXXX" deviceAlias="YYYY" />
- * >remembered-wifi-displays>
- * >/display-manager-state>
+ * <remembered-wifi-displays>
+ * <display-states>
+ * <display>
+ * <color-mode>0</color-mode>
+ * </display>
+ * </display-states>
+ * </display-manager-state>
* </code>
*
* TODO: refactor this to extract common code shared with the input manager's data store
@@ -62,6 +71,10 @@
// Remembered Wifi display devices.
private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>();
+ // Display state by unique id.
+ private final HashMap<String, DisplayState> mDisplayStates =
+ new HashMap<String, DisplayState>();
+
// The atomic file used to safely read or write the file.
private final AtomicFile mAtomicFile;
@@ -168,7 +181,41 @@
return -1;
}
- private void loadIfNeeded() {
+ public int getColorMode(DisplayDevice device) {
+ if (!device.hasStableUniqueId()) {
+ return Display.COLOR_MODE_DEFAULT;
+ }
+ DisplayState state = getDisplayState(device.getUniqueId(), false);
+ if (state == null) {
+ return Display.COLOR_MODE_DEFAULT;
+ }
+ return state.getColorMode();
+ }
+
+ public boolean setColorMode(DisplayDevice device, int colorMode) {
+ if (!device.hasStableUniqueId()) {
+ return false;
+ }
+ DisplayState state = getDisplayState(device.getUniqueId(), true);
+ if (state.setColorMode(colorMode)) {
+ setDirty();
+ return true;
+ }
+ return false;
+ }
+
+ private DisplayState getDisplayState(String uniqueId, boolean createIfAbsent) {
+ loadIfNeeded();
+ DisplayState state = mDisplayStates.get(uniqueId);
+ if (state == null && createIfAbsent) {
+ state = new DisplayState();
+ mDisplayStates.put(uniqueId, state);
+ setDirty();
+ }
+ return state;
+ }
+
+ public void loadIfNeeded() {
if (!mLoaded) {
load();
mLoaded = true;
@@ -240,6 +287,9 @@
if (parser.getName().equals("remembered-wifi-displays")) {
loadRememberedWifiDisplaysFromXml(parser);
}
+ if (parser.getName().equals("display-states")) {
+ loadDisplaysFromXml(parser);
+ }
}
}
@@ -267,6 +317,27 @@
}
}
+ private void loadDisplaysFromXml(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ if (parser.getName().equals("display")) {
+ String uniqueId = parser.getAttributeValue(null, "unique-id");
+ if (uniqueId == null) {
+ throw new XmlPullParserException(
+ "Missing unique-id attribute on display.");
+ }
+ if (mDisplayStates.containsKey(uniqueId)) {
+ throw new XmlPullParserException("Found duplicate display.");
+ }
+
+ DisplayState state = new DisplayState();
+ state.loadFromXml(parser);
+ mDisplayStates.put(uniqueId, state);
+ }
+ }
+ }
+
private void saveToXml(XmlSerializer serializer) throws IOException {
serializer.startDocument(null, true);
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
@@ -282,7 +353,72 @@
serializer.endTag(null, "wifi-display");
}
serializer.endTag(null, "remembered-wifi-displays");
+ serializer.startTag(null, "display-states");
+ for (Map.Entry<String, DisplayState> entry : mDisplayStates.entrySet()) {
+ final String uniqueId = entry.getKey();
+ final DisplayState state = entry.getValue();
+ serializer.startTag(null, "display");
+ serializer.attribute(null, "unique-id", uniqueId);
+ state.saveToXml(serializer);
+ serializer.endTag(null, "display");
+ }
+ serializer.endTag(null, "display-states");
serializer.endTag(null, "display-manager-state");
serializer.endDocument();
}
+
+ public void dump(PrintWriter pw) {
+ pw.println("PersistentDataStore");
+ pw.println(" mLoaded=" + mLoaded);
+ pw.println(" mDirty=" + mDirty);
+ pw.println(" RememberedWifiDisplays:");
+ int i = 0;
+ for (WifiDisplay display : mRememberedWifiDisplays) {
+ pw.println(" " + i++ + ": " + display);
+ }
+ pw.println(" DisplayStates:");
+ i = 0;
+ for (Map.Entry<String, DisplayState> entry : mDisplayStates.entrySet()) {
+ pw.println(" " + i++ + ": " + entry.getKey());
+ entry.getValue().dump(pw, " ");
+ }
+ }
+
+ private static final class DisplayState {
+ private int mColorMode;
+
+ public boolean setColorMode(int colorMode) {
+ if (colorMode == mColorMode) {
+ return false;
+ }
+ mColorMode = colorMode;
+ return true;
+ }
+
+ public int getColorMode() {
+ return mColorMode;
+ }
+
+ public void loadFromXml(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ if (parser.getName().equals("color-mode")) {
+ String value = parser.nextText();
+ mColorMode = Integer.parseInt(value);
+ }
+ }
+ }
+
+ public void saveToXml(XmlSerializer serializer) throws IOException {
+ serializer.startTag(null, "color-mode");
+ serializer.text(Integer.toString(mColorMode));
+ serializer.endTag(null, "color-mode");
+ }
+
+ private void dump(final PrintWriter pw, final String prefix) {
+ pw.println(prefix + "ColorMode=" + mColorMode);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 986efd69..9d0fde5 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -225,6 +225,11 @@
}
@Override
+ public boolean hasStableUniqueId() {
+ return false;
+ }
+
+ @Override
public Runnable requestDisplayStateLocked(int state, int brightness) {
if (state != mDisplayState) {
mDisplayState = state;
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index 64bc729..08c0a1a 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -602,6 +602,11 @@
mMode = createMode(width, height, refreshRate);
}
+ @Override
+ public boolean hasStableUniqueId() {
+ return true;
+ }
+
public void destroyLocked() {
if (mSurface != null) {
mSurface.release();
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 1f6616e..a783fa2 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -20,6 +20,7 @@
import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import android.Manifest;
@@ -32,6 +33,8 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
+import android.database.ContentObserver;
+import android.hardware.input.InputManagerInternal;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -111,11 +114,16 @@
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ writePulseGestureEnabled();
synchronized (mLock) {
stopDreamLocked(false /*immediate*/);
}
}
}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.DOZE_ENABLED), false,
+ mDozeEnabledObserver, UserHandle.USER_ALL);
+ writePulseGestureEnabled();
}
}
@@ -414,6 +422,12 @@
}
}
+ private void writePulseGestureEnabled() {
+ ComponentName name = getDozeComponent();
+ boolean dozeEnabled = validateDream(name);
+ LocalServices.getService(InputManagerInternal.class).setPulseGestureEnabled(dozeEnabled);
+ }
+
private static String componentsToString(ComponentName[] componentNames) {
StringBuilder names = new StringBuilder();
if (componentNames != null) {
@@ -450,6 +464,13 @@
}
};
+ private final ContentObserver mDozeEnabledObserver = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ writePulseGestureEnabled();
+ }
+ };
+
/**
* Handler for asynchronous operations performed by the dream manager.
* Ensures operations to {@link DreamController} are single-threaded.
diff --git a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
index c04b9a1..87da866 100644
--- a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
+++ b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
@@ -39,9 +39,9 @@
public abstract void resetFailedAttempts();
public AuthenticationClient(Context context, long halDeviceId, IBinder token,
- IFingerprintServiceReceiver receiver, int callingUserId, int groupId, long opId,
+ IFingerprintServiceReceiver receiver, int targetUserId, int groupId, long opId,
boolean restricted, String owner) {
- super(context, halDeviceId, token, receiver, callingUserId, groupId, restricted, owner);
+ super(context, halDeviceId, token, receiver, targetUserId, groupId, restricted, owner);
mOpId = opId;
}
@@ -65,7 +65,7 @@
Fingerprint fp = !getIsRestricted()
? new Fingerprint("" /* TODO */, groupId, fingerId, getHalDeviceId())
: null;
- receiver.onAuthenticationSucceeded(getHalDeviceId(), fp);
+ receiver.onAuthenticationSucceeded(getHalDeviceId(), fp, getTargetUserId());
}
} catch (RemoteException e) {
Slog.w(TAG, "Failed to notify Authenticated:", e);
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 081a3af..73c8469 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -72,6 +72,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
/**
@@ -90,10 +91,17 @@
private static final String ACTION_LOCKOUT_RESET =
"com.android.server.fingerprint.ACTION_LOCKOUT_RESET";
+ private class PerformanceStats {
+ int accept; // number of accepted fingerprints
+ int reject; // number of rejected fingerprints
+ int acquire; // total number of acquisitions. Should be >= accept+reject due to poor image
+ // acquisition in some cases (too fast, too slow, dirty sensor, etc.)
+ int lockout; // total number of lockouts
+ }
+
private final ArrayList<FingerprintServiceLockoutResetMonitor> mLockoutMonitors =
new ArrayList<>();
private final AppOpsManager mAppOps;
-
private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30*1000;
private static final int MAX_FAILED_ATTEMPTS = 5;
private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
@@ -110,6 +118,15 @@
private ClientMonitor mCurrentClient;
private ClientMonitor mPendingClient;
private long mCurrentAuthenticatorId;
+ private PerformanceStats mPerformanceStats;
+
+ // Normal fingerprint authentications are tracked by mPerformanceMap.
+ private HashMap<Integer, PerformanceStats> mPerformanceMap
+ = new HashMap<Integer, PerformanceStats>();
+
+ // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
+ private HashMap<Integer, PerformanceStats> mCryptoPerformanceMap
+ = new HashMap<Integer, PerformanceStats>();
private Handler mHandler = new Handler() {
@Override
@@ -246,6 +263,11 @@
if (client != null && client.onAuthenticated(fingerId, groupId)) {
removeClient(client);
}
+ if (fingerId != 0) {
+ mPerformanceStats.accept++;
+ } else {
+ mPerformanceStats.reject++;
+ }
}
protected void handleAcquired(long deviceId, int acquiredInfo) {
@@ -253,6 +275,11 @@
if (client != null && client.onAcquired(acquiredInfo)) {
removeClient(client);
}
+ if (mPerformanceStats != null && !inLockoutMode()
+ && client instanceof AuthenticationClient) {
+ // ignore enrollment acquisitions or acquisitions when we're locked out
+ mPerformanceStats.acquire++;
+ }
}
protected void handleEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
@@ -443,10 +470,10 @@
/**
* @param opPackageName name of package for caller
- * @param foregroundOnly only allow this call while app is in the foreground
+ * @param requireForeground only allow this call while app is in the foreground
* @return true if caller can use fingerprint API
*/
- private boolean canUseFingerprint(String opPackageName, boolean foregroundOnly, int uid,
+ private boolean canUseFingerprint(String opPackageName, boolean requireForeground, int uid,
int pid) {
checkPermission(USE_FINGERPRINT);
if (isKeyguard(opPackageName)) {
@@ -461,7 +488,7 @@
Slog.w(TAG, "Rejecting " + opPackageName + " ; permission denied");
return false;
}
- if (foregroundOnly && !isForegroundActivity(uid, pid)) {
+ if (requireForeground && !(isForegroundActivity(uid, pid) || currentClient(opPackageName))){
Slog.w(TAG, "Rejecting " + opPackageName + " ; not in foreground");
return false;
}
@@ -469,6 +496,14 @@
}
/**
+ * @param opPackageName package of the caller
+ * @return true if this is the same client currently using fingerprint
+ */
+ private boolean currentClient(String opPackageName) {
+ return mCurrentClient != null && mCurrentClient.getOwnerString().equals(opPackageName);
+ }
+
+ /**
* @param clientPackage
* @return true if this is keyguard package
*/
@@ -501,10 +536,13 @@
if (DEBUG) Slog.v(TAG, "startAuthentication(" + opPackageName + ")");
AuthenticationClient client = new AuthenticationClient(getContext(), mHalDeviceId, token,
- receiver, callingUserId, groupId, opId, restricted, opPackageName) {
+ receiver, mCurrentUserId, groupId, opId, restricted, opPackageName) {
@Override
public boolean handleFailedAttempt() {
mFailedAttempts++;
+ if (mFailedAttempts == MAX_FAILED_ATTEMPTS) {
+ mPerformanceStats.lockout++;
+ }
if (inLockoutMode()) {
// Failing multiple times will continue to push out the lockout time.
scheduleLockoutReset();
@@ -742,12 +780,24 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0);
if (!canUseFingerprint(opPackageName, true /* foregroundOnly */,
callingUid, pid)) {
if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
return;
}
+
+ MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0);
+
+ // Get performance stats object for this user.
+ HashMap<Integer, PerformanceStats> pmap
+ = (opId == 0) ? mPerformanceMap : mCryptoPerformanceMap;
+ PerformanceStats stats = pmap.get(mCurrentUserId);
+ if (stats == null) {
+ stats = new PerformanceStats();
+ pmap.put(mCurrentUserId, stats);
+ }
+ mPerformanceStats = stats;
+
startAuthentication(token, opId, callingUserId, groupId, receiver,
flags, restricted, opPackageName);
}
@@ -924,9 +974,21 @@
for (UserInfo user : UserManager.get(getContext()).getUsers()) {
final int userId = user.getUserHandle().getIdentifier();
final int N = mFingerprintUtils.getFingerprintsForUser(mContext, userId).size();
+ PerformanceStats stats = mPerformanceMap.get(userId);
+ PerformanceStats cryptoStats = mCryptoPerformanceMap.get(userId);
JSONObject set = new JSONObject();
set.put("id", userId);
set.put("count", N);
+ set.put("accept", (stats != null) ? stats.accept : 0);
+ set.put("reject", (stats != null) ? stats.reject : 0);
+ set.put("acquire", (stats != null) ? stats.acquire : 0);
+ set.put("lockout", (stats != null) ? stats.lockout : 0);
+ // cryptoStats measures statistics about secure fingerprint transactions
+ // (e.g. to unlock password storage, make secure purchases, etc.)
+ set.put("acceptCrypto", (cryptoStats != null) ? cryptoStats.accept : 0);
+ set.put("rejectCrypto", (cryptoStats != null) ? cryptoStats.reject : 0);
+ set.put("acquireCrypto", (cryptoStats != null) ? cryptoStats.acquire : 0);
+ set.put("lockoutCrypto", (cryptoStats != null) ? cryptoStats.lockout : 0);
sets.put(set);
}
@@ -947,6 +1009,7 @@
private void updateActiveGroup(int userId, String clientPackage) {
IFingerprintDaemon daemon = getFingerprintDaemon();
+
if (daemon != null) {
try {
userId = getUserOrWorkProfileId(clientPackage, userId);
@@ -1013,7 +1076,7 @@
public void onForegroundProfileSwitch(int newProfileId) {
// Ignore.
}
- });
+ }, TAG);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to listen for user switching event" ,e);
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index aa1d73f..74095ac 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -18,7 +18,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Build;
import android.os.LocaleList;
+import android.util.Log;
import android.view.Display;
import com.android.internal.inputmethod.InputMethodSubtypeHandle;
import com.android.internal.os.SomeArgs;
@@ -98,8 +100,11 @@
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.FileReader;
+import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -109,6 +114,7 @@
import java.util.List;
import java.util.Locale;
+import libcore.io.IoUtils;
import libcore.io.Streams;
import libcore.util.Objects;
@@ -136,6 +142,8 @@
private final Context mContext;
private final InputManagerHandler mHandler;
+ private final File mDoubleTouchGestureEnableFile;
+
private WindowManagerCallbacks mWindowManagerCallbacks;
private WiredAccessoryCallbacks mWiredAccessoryCallbacks;
private boolean mSystemReady;
@@ -301,6 +309,11 @@
+ mUseDevInputEventForAudioJack);
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
+ String doubleTouchGestureEnablePath = context.getResources().getString(
+ R.string.config_doubleTouchGestureEnableFile);
+ mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :
+ new File(doubleTouchGestureEnablePath);
+
LocalServices.addService(InputManagerInternal.class, new LocalService());
}
@@ -2279,5 +2292,20 @@
public void toggleCapsLock(int deviceId) {
nativeToggleCapsLock(mPtr, deviceId);
}
+
+ @Override
+ public void setPulseGestureEnabled(boolean enabled) {
+ if (mDoubleTouchGestureEnableFile != null) {
+ FileWriter writer = null;
+ try {
+ writer = new FileWriter(mDoubleTouchGestureEnableFile);
+ writer.write(enabled ? "1" : "0");
+ } catch (IOException e) {
+ Log.wtf(TAG, "Unable to setPulseGestureEnabled", e);
+ } finally {
+ IoUtils.closeQuietly(writer);
+ }
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/job/JobPackageTracker.java b/services/core/java/com/android/server/job/JobPackageTracker.java
index ba96b74..82e2eb4 100644
--- a/services/core/java/com/android/server/job/JobPackageTracker.java
+++ b/services/core/java/com/android/server/job/JobPackageTracker.java
@@ -395,7 +395,10 @@
return 0;
}
final long now = SystemClock.uptimeMillis();
- long time = cur.getActiveTime(now) + cur.getPendingTime(now);
+ long time = 0;
+ if (cur != null) {
+ time += cur.getActiveTime(now) + cur.getPendingTime(now);
+ }
long period = mCurDataSet.getTotalTime(now);
if (last != null) {
time += last.getActiveTime(now) + last.getPendingTime(now);
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 8589de1..9d93146 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -28,6 +28,7 @@
import java.util.Iterator;
import java.util.List;
+import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
@@ -43,7 +44,9 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -396,10 +399,11 @@
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
if (DEBUG) {
- Slog.d(TAG, "Receieved: " + intent.getAction());
+ Slog.d(TAG, "Receieved: " + action);
}
- if (Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())) {
+ if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
// Purge the app's jobs if the whole package was just disabled. When this is
// the case the component name will be a bare package name.
final String pkgName = getPackageName(intent);
@@ -433,7 +437,7 @@
} else {
Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
}
- } else if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+ } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
// If this is an outright uninstall rather than the first half of an
// app update sequence, cancel the jobs associated with the app.
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
@@ -443,12 +447,43 @@
}
cancelJobsForUid(uidRemoved, true);
}
- } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
+ } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
if (DEBUG) {
Slog.d(TAG, "Removing jobs for user: " + userId);
}
cancelJobsForUser(userId);
+ } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
+ // Has this package scheduled any jobs, such that we will take action
+ // if it were to be force-stopped?
+ final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ final String pkgName = intent.getData().getSchemeSpecificPart();
+ if (pkgUid != -1) {
+ List<JobStatus> jobsForUid;
+ synchronized (mLock) {
+ jobsForUid = mJobs.getJobsByUid(pkgUid);
+ }
+ for (int i = jobsForUid.size() - 1; i >= 0; i--) {
+ if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
+ + pkgUid + " has jobs");
+ }
+ setResultCode(Activity.RESULT_OK);
+ break;
+ }
+ }
+ }
+ } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
+ // possible force-stop
+ final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ final String pkgName = intent.getData().getSchemeSpecificPart();
+ if (pkgUid != -1) {
+ if (DEBUG) {
+ Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
+ }
+ cancelJobsForPackageAndUid(pkgName, pkgUid);
+ }
}
}
};
@@ -583,6 +618,19 @@
}
}
+ void cancelJobsForPackageAndUid(String pkgName, int uid) {
+ List<JobStatus> jobsForUid;
+ synchronized (mLock) {
+ jobsForUid = mJobs.getJobsByUid(uid);
+ }
+ for (int i = jobsForUid.size() - 1; i >= 0; i--) {
+ final JobStatus job = jobsForUid.get(i);
+ if (job.getSourcePackageName().equals(pkgName)) {
+ cancelJobImpl(job, null);
+ }
+ }
+ }
+
/**
* Entry point from client to cancel all jobs originating from their uid.
* This will remove the job from the master list, and cancel the job if it was staged for
@@ -754,6 +802,8 @@
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+ filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
filter.addDataScheme("package");
getContext().registerReceiverAsUser(
mBroadcastReceiver, UserHandle.ALL, filter, null, null);
@@ -1556,6 +1606,11 @@
}
}
+ if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
+ }
+
long ident = Binder.clearCallingIdentity();
try {
return JobSchedulerService.this.schedule(job, uid);
diff --git a/services/core/java/com/android/server/job/controllers/ContentObserverController.java b/services/core/java/com/android/server/job/controllers/ContentObserverController.java
index a42d0cd..5d209fc 100644
--- a/services/core/java/com/android/server/job/controllers/ContentObserverController.java
+++ b/services/core/java/com/android/server/job/controllers/ContentObserverController.java
@@ -16,6 +16,7 @@
package com.android.server.job.controllers;
+import android.annotation.UserIdInt;
import android.app.job.JobInfo;
import android.content.Context;
import android.database.ContentObserver;
@@ -23,6 +24,7 @@
import android.os.Handler;
import android.os.UserHandle;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -35,6 +37,7 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
/**
* Controller for monitoring changes to content URIs through a ContentObserver.
@@ -59,7 +62,11 @@
private static volatile ContentObserverController sController;
final private List<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
- ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> mObservers = new ArrayMap<>();
+ /**
+ * Per-userid {@link JobInfo.TriggerContentUri} keyed ContentObserver cache.
+ */
+ SparseArray<ArrayMap<JobInfo.TriggerContentUri, ObserverInstance>> mObservers =
+ new SparseArray<>();
final Handler mHandler;
public static ContentObserverController get(JobSchedulerService taskManagerService) {
@@ -203,18 +210,21 @@
final class ObserverInstance extends ContentObserver {
final JobInfo.TriggerContentUri mUri;
+ final @UserIdInt int mUserId;
final ArraySet<JobInstance> mJobs = new ArraySet<>();
- public ObserverInstance(Handler handler, JobInfo.TriggerContentUri uri) {
+ public ObserverInstance(Handler handler, JobInfo.TriggerContentUri uri,
+ @UserIdInt int userId) {
super(handler);
mUri = uri;
+ mUserId = userId;
}
@Override
public void onChange(boolean selfChange, Uri uri) {
if (DEBUG) {
Slog.i(TAG, "onChange(self=" + selfChange + ") for " + uri
- + " when mUri=" + mUri);
+ + " when mUri=" + mUri + " mUserId=" + mUserId);
}
synchronized (mLock) {
final int N = mJobs.size();
@@ -258,27 +268,38 @@
boolean mTriggerPending;
+ // This constructor must be called with the master job scheduler lock held.
JobInstance(JobStatus jobStatus) {
mJobStatus = jobStatus;
mExecuteRunner = new TriggerRunnable(this);
mTimeoutRunner = new TriggerRunnable(this);
final JobInfo.TriggerContentUri[] uris = jobStatus.getJob().getTriggerContentUris();
+ final int sourceUserId = jobStatus.getSourceUserId();
+ ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> observersOfUser =
+ mObservers.get(sourceUserId);
+ if (observersOfUser == null) {
+ observersOfUser = new ArrayMap<>();
+ mObservers.put(sourceUserId, observersOfUser);
+ }
if (uris != null) {
for (JobInfo.TriggerContentUri uri : uris) {
- ObserverInstance obs = mObservers.get(uri);
+ ObserverInstance obs = observersOfUser.get(uri);
if (obs == null) {
- obs = new ObserverInstance(mHandler, uri);
- mObservers.put(uri, obs);
+ obs = new ObserverInstance(mHandler, uri, jobStatus.getSourceUserId());
+ observersOfUser.put(uri, obs);
final boolean andDescendants = (uri.getFlags() &
JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS) != 0;
if (DEBUG) {
Slog.v(TAG, "New observer " + obs + " for " + uri.getUri()
- + " andDescendants=" + andDescendants);
+ + " andDescendants=" + andDescendants
+ + " sourceUserId=" + sourceUserId);
}
mContext.getContentResolver().registerContentObserver(
uri.getUri(),
andDescendants,
- obs);
+ obs,
+ sourceUserId
+ );
} else {
if (DEBUG) {
final boolean andDescendants = (uri.getFlags() &
@@ -342,7 +363,11 @@
Slog.i(TAG, "Unregistering observer " + obs + " for " + obs.mUri.getUri());
}
mContext.getContentResolver().unregisterContentObserver(obs);
- mObservers.remove(obs.mUri);
+ ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> observerOfUser =
+ mObservers.get(obs.mUserId);
+ if (observerOfUser != null) {
+ observerOfUser.remove(obs.mUri);
+ }
}
}
}
@@ -366,60 +391,66 @@
int N = mObservers.size();
if (N > 0) {
pw.println(" Observers:");
- for (int i = 0; i < N; i++) {
- ObserverInstance obs = mObservers.valueAt(i);
- int M = obs.mJobs.size();
- boolean shouldDump = false;
- for (int j=0; j<M; j++) {
- JobInstance inst = obs.mJobs.valueAt(j);
- if (inst.mJobStatus.shouldDump(filterUid)) {
- shouldDump = true;
- break;
+ for (int userIdx = 0; userIdx < N; userIdx++) {
+ final int userId = mObservers.keyAt(userIdx);
+ ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> observersOfUser =
+ mObservers.get(userId);
+ int numbOfObserversPerUser = observersOfUser.size();
+ for (int observerIdx = 0 ; observerIdx < numbOfObserversPerUser; observerIdx++) {
+ ObserverInstance obs = observersOfUser.valueAt(observerIdx);
+ int M = obs.mJobs.size();
+ boolean shouldDump = false;
+ for (int j = 0; j < M; j++) {
+ JobInstance inst = obs.mJobs.valueAt(j);
+ if (inst.mJobStatus.shouldDump(filterUid)) {
+ shouldDump = true;
+ break;
+ }
}
- }
- if (!shouldDump) {
- continue;
- }
- pw.print(" ");
- JobInfo.TriggerContentUri trigger = mObservers.keyAt(i);
- pw.print(trigger.getUri());
- pw.print(" 0x");
- pw.print(Integer.toHexString(trigger.getFlags()));
- pw.print(" (");
- pw.print(System.identityHashCode(obs));
- pw.println("):");
- pw.println(" Jobs:");
- for (int j=0; j<M; j++) {
- JobInstance inst = obs.mJobs.valueAt(j);
- pw.print(" #");
- inst.mJobStatus.printUniqueId(pw);
- pw.print(" from ");
- UserHandle.formatUid(pw, inst.mJobStatus.getSourceUid());
- if (inst.mChangedAuthorities != null) {
- pw.println(":");
- if (inst.mTriggerPending) {
- pw.print(" Trigger pending: update=");
- TimeUtils.formatDuration(
- inst.mJobStatus.getTriggerContentUpdateDelay(), pw);
- pw.print(", max=");
- TimeUtils.formatDuration(
- inst.mJobStatus.getTriggerContentMaxDelay(), pw);
+ if (!shouldDump) {
+ continue;
+ }
+ pw.print(" ");
+ JobInfo.TriggerContentUri trigger = observersOfUser.keyAt(observerIdx);
+ pw.print(trigger.getUri());
+ pw.print(" 0x");
+ pw.print(Integer.toHexString(trigger.getFlags()));
+ pw.print(" (");
+ pw.print(System.identityHashCode(obs));
+ pw.println("):");
+ pw.println(" Jobs:");
+ for (int j = 0; j < M; j++) {
+ JobInstance inst = obs.mJobs.valueAt(j);
+ pw.print(" #");
+ inst.mJobStatus.printUniqueId(pw);
+ pw.print(" from ");
+ UserHandle.formatUid(pw, inst.mJobStatus.getSourceUid());
+ if (inst.mChangedAuthorities != null) {
+ pw.println(":");
+ if (inst.mTriggerPending) {
+ pw.print(" Trigger pending: update=");
+ TimeUtils.formatDuration(
+ inst.mJobStatus.getTriggerContentUpdateDelay(), pw);
+ pw.print(", max=");
+ TimeUtils.formatDuration(
+ inst.mJobStatus.getTriggerContentMaxDelay(), pw);
+ pw.println();
+ }
+ pw.println(" Changed Authorities:");
+ for (int k = 0; k < inst.mChangedAuthorities.size(); k++) {
+ pw.print(" ");
+ pw.println(inst.mChangedAuthorities.valueAt(k));
+ }
+ if (inst.mChangedUris != null) {
+ pw.println(" Changed URIs:");
+ for (int k = 0; k < inst.mChangedUris.size(); k++) {
+ pw.print(" ");
+ pw.println(inst.mChangedUris.valueAt(k));
+ }
+ }
+ } else {
pw.println();
}
- pw.println(" Changed Authorities:");
- for (int k=0; k<inst.mChangedAuthorities.size(); k++) {
- pw.print(" ");
- pw.println(inst.mChangedAuthorities.valueAt(k));
- }
- if (inst.mChangedUris != null) {
- pw.println(" Changed URIs:");
- for (int k = 0; k<inst.mChangedUris.size(); k++) {
- pw.print(" ");
- pw.println(inst.mChangedUris.valueAt(k));
- }
- }
- } else {
- pw.println();
}
}
}
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 552c990..0947554 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -486,6 +486,7 @@
+ ":[" + job.getService()
+ ",jId=" + job.getId()
+ ",u" + getUserId()
+ + ",suid=" + getSourceUid()
+ ",R=(" + formatRunTime(earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME)
+ "," + formatRunTime(latestRunTimeElapsedMillis, NO_LATEST_RUNTIME) + ")"
+ ",N=" + job.getNetworkType() + ",C=" + job.isRequireCharging()
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 173f76f..7580cf4 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -393,6 +393,15 @@
// SIM/Carrier info.
private final static String SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
+ // Persist property for LPP_PROFILE
+ private final static String LPP_PROFILE = "persist.sys.gps.lpp";
+
+ // VZW PLMN info
+ private static final String[] VzwMccMncList = {"311480", "310004", "20404"};
+ // corresponding GID1 value, empty string means ignore gid1 match.
+ private static final String[] VzwGid1List = {"", "", "BAE0000000000000"};
+
+
private final PowerManager mPowerManager;
private final AlarmManager mAlarmManager;
private final PendingIntent mWakeupIntent;
@@ -507,14 +516,43 @@
}
};
+ private final boolean isVerizon(String mccMnc, String imsi, String groupId) {
+ if (DEBUG) Log.d(TAG, "simOperator: " + mccMnc);
+ if (!TextUtils.isEmpty(mccMnc) || !TextUtils.isEmpty(imsi)) {
+ for (int i = 0; i < VzwMccMncList.length; i++) {
+ if ((!TextUtils.isEmpty(mccMnc) && mccMnc.equals(VzwMccMncList[i])) ||
+ (!TextUtils.isEmpty(imsi) && imsi.startsWith(VzwMccMncList[i]))) {
+ // check gid too if needed
+ if (TextUtils.isEmpty(VzwGid1List[i]) || VzwGid1List[i].equals(groupId)) {
+ if (DEBUG) Log.d(TAG, "Verizon UICC");
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
private void subscriptionOrSimChanged(Context context) {
if (DEBUG) Log.d(TAG, "received SIM related action: ");
TelephonyManager phone = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
String mccMnc = phone.getSimOperator();
+ String imsi = phone.getSubscriberId();
+ String groupId = phone.getGroupIdLevel1();
if (!TextUtils.isEmpty(mccMnc)) {
if (DEBUG) Log.d(TAG, "SIM MCC/MNC is available: " + mccMnc);
synchronized (mLock) {
+ if (isVerizon(mccMnc, imsi, groupId)) {
+ // load current properties for carrier VZW
+ loadPropertiesFromResource(context, mProperties);
+ String lpp_profile = mProperties.getProperty("LPP_PROFILE");
+ // set the persist property LPP_PROFILE for VZW
+ SystemProperties.set(LPP_PROFILE, lpp_profile);
+ } else {
+ // reset the persist property for Non VZW
+ SystemProperties.set(LPP_PROFILE, "");
+ }
reloadGpsProperties(context, mProperties);
mNIHandler.setSuplEsEnabled(mSuplEsEnabled);
}
@@ -571,8 +609,10 @@
private void reloadGpsProperties(Context context, Properties properties) {
if (DEBUG) Log.d(TAG, "Reset GPS properties, previous size = " + properties.size());
loadPropertiesFromResource(context, properties);
+
boolean isPropertiesLoadedFromFile = false;
final String gpsHardware = SystemProperties.get("ro.hardware.gps");
+
if (!TextUtils.isEmpty(gpsHardware)) {
final String propFilename =
PROPERTIES_FILE_PREFIX + "." + gpsHardware + PROPERTIES_FILE_SUFFIX;
@@ -583,7 +623,11 @@
loadPropertiesFromFile(DEFAULT_PROPERTIES_FILE, properties);
}
if (DEBUG) Log.d(TAG, "GPS properties reloaded, size = " + properties.size());
-
+ String lpp_prof = SystemProperties.get(LPP_PROFILE);
+ if (!TextUtils.isEmpty(lpp_prof)) {
+ // override default value of this if lpp_prof is not empty
+ properties.setProperty("LPP_PROFILE", lpp_prof);
+ }
// TODO: we should get rid of C2K specific setting.
setSuplHostPort(properties.getProperty("SUPL_HOST"),
properties.getProperty("SUPL_PORT"));
diff --git a/services/core/java/com/android/server/media/MediaResourceMonitorService.java b/services/core/java/com/android/server/media/MediaResourceMonitorService.java
index e169d63..0eb8b55 100644
--- a/services/core/java/com/android/server/media/MediaResourceMonitorService.java
+++ b/services/core/java/com/android/server/media/MediaResourceMonitorService.java
@@ -24,6 +24,7 @@
import android.os.Binder;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.UserManager;
import android.util.Log;
import android.util.Slog;
import com.android.server.SystemService;
@@ -59,12 +60,20 @@
final long identity = Binder.clearCallingIdentity();
try {
String pkgNames[] = getPackageNamesFromPid(pid);
- if (pkgNames != null) {
- Intent intent = new Intent(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
- intent.putExtra(Intent.EXTRA_PACKAGES, pkgNames);
- intent.putExtra(Intent.EXTRA_MEDIA_RESOURCE_TYPE, type);
- getContext().sendBroadcastAsUser(intent,
- new UserHandle(ActivityManager.getCurrentUser()),
+ if (pkgNames == null) {
+ return;
+ }
+ UserManager manager = (UserManager) getContext().getSystemService(
+ Context.USER_SERVICE);
+ int[] userIds = manager.getEnabledProfileIds(ActivityManager.getCurrentUser());
+ if (userIds == null || userIds.length == 0) {
+ return;
+ }
+ Intent intent = new Intent(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
+ intent.putExtra(Intent.EXTRA_PACKAGES, pkgNames);
+ intent.putExtra(Intent.EXTRA_MEDIA_RESOURCE_TYPE, type);
+ for (int userId : userIds) {
+ getContext().sendBroadcastAsUser(intent, UserHandle.of(userId),
android.Manifest.permission.RECEIVE_MEDIA_RESOURCE_USAGE);
}
} finally {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 29c54e9..88d6c14 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -454,7 +454,7 @@
@Override
public String toString() {
- return mPackageName + "/" + mTag;
+ return mPackageName + "/" + mTag + " (uid=" + mUserId + ")";
}
private void postAdjustLocalVolume(final int stream, final int direction, final int flags,
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index a4d2cd2..777eee8 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -47,10 +47,12 @@
import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.speech.RecognizerIntent;
import android.text.TextUtils;
@@ -67,6 +69,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -97,7 +100,9 @@
private ContentResolver mContentResolver;
private SettingsObserver mSettingsObserver;
- private int mCurrentUserId = -1;
+ // List of user IDs running in the foreground.
+ // Multiple users can be in the foreground if the work profile is on.
+ private final List<Integer> mCurrentUserIdList = new ArrayList<>();
// Used to notify system UI when remote volume was changed. TODO find a
// better way to handle this.
@@ -181,22 +186,26 @@
}
@Override
- public void onStartUser(int userHandle) {
+ public void onStartUser(int userId) {
+ if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
updateUser();
}
@Override
- public void onSwitchUser(int userHandle) {
+ public void onSwitchUser(int userId) {
+ if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
updateUser();
}
@Override
- public void onStopUser(int userHandle) {
+ public void onStopUser(int userId) {
+ if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
synchronized (mLock) {
- UserRecord user = mUserRecords.get(userHandle);
+ UserRecord user = mUserRecords.get(userId);
if (user != null) {
destroyUserLocked(user);
}
+ updateUser();
}
}
@@ -228,18 +237,24 @@
private void updateUser() {
synchronized (mLock) {
- int userId = ActivityManager.getCurrentUser();
- if (mCurrentUserId != userId) {
- final int oldUserId = mCurrentUserId;
- mCurrentUserId = userId; // do this first
-
- UserRecord oldUser = mUserRecords.get(oldUserId);
- if (oldUser != null) {
- oldUser.stopLocked();
+ UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
+ int currentUser = ActivityManager.getCurrentUser();
+ // Include all profiles even though they aren't yet enabled to handle work profile case.
+ int[] userIds = manager.getProfileIdsWithDisabled(currentUser);
+ mCurrentUserIdList.clear();
+ if (userIds != null && userIds.length > 0) {
+ for (int userId : userIds) {
+ mCurrentUserIdList.add(userId);
}
-
- UserRecord newUser = getOrCreateUser(userId);
- newUser.startLocked();
+ } else {
+ // This shouldn't happen.
+ Log.w(TAG, "Failed to get enabled profiles.");
+ mCurrentUserIdList.add(currentUser);
+ }
+ for (int userId : mCurrentUserIdList) {
+ if (mUserRecords.get(userId) == null) {
+ mUserRecords.put(userId, new UserRecord(getContext(), userId));
+ }
}
}
}
@@ -272,7 +287,6 @@
* @param user The user to dispose of
*/
private void destroyUserLocked(UserRecord user) {
- user.stopLocked();
user.destroyLocked();
mUserRecords.remove(user.mUserId);
}
@@ -427,6 +441,12 @@
private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
String callerPackageName, ISessionCallback cb, String tag) {
+ UserRecord user = mUserRecords.get(userId);
+ if (user == null) {
+ Log.wtf(TAG, "Request from invalid user: " + userId);
+ throw new RuntimeException("Session request from invalid user.");
+ }
+
final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
callerPackageName, cb, tag, this, mHandler);
try {
@@ -436,9 +456,7 @@
}
mAllSessions.add(session);
- mPriorityStack.addSession(session);
-
- UserRecord user = getOrCreateUser(userId);
+ mPriorityStack.addSession(session, mCurrentUserIdList.contains(userId));
user.addSessionLocked(session);
mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);
@@ -449,15 +467,6 @@
return session;
}
- private UserRecord getOrCreateUser(int userId) {
- UserRecord user = mUserRecords.get(userId);
- if (user == null) {
- user = new UserRecord(getContext(), userId);
- mUserRecords.put(userId, user);
- }
- return user;
- }
-
private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
@@ -536,13 +545,6 @@
restoreMediaButtonReceiver();
}
- public void startLocked() {
- }
-
- public void stopLocked() {
- // nothing for now
- }
-
public void destroyLocked() {
for (int i = mSessions.size() - 1; i >= 0; i--) {
MediaSessionRecord session = mSessions.get(i);
@@ -578,7 +580,7 @@
private void restoreMediaButtonReceiver() {
String receiverName = Settings.Secure.getStringForUser(mContentResolver,
- Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT);
+ Settings.System.MEDIA_BUTTON_RECEIVER, mUserId);
if (!TextUtils.isEmpty(receiverName)) {
ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
if (eventReceiver == null) {
@@ -767,12 +769,22 @@
synchronized (mLock) {
// If we don't have a media button receiver to fall back on
// include non-playing sessions for dispatching
- UserRecord ur = mUserRecords.get(mCurrentUserId);
- boolean useNotPlayingSessions = (ur == null) ||
- (ur.mLastMediaButtonReceiver == null
- && ur.mRestoredMediaButtonReceiver == null);
- MediaSessionRecord session = mPriorityStack
- .getDefaultMediaButtonSession(mCurrentUserId, useNotPlayingSessions);
+ boolean useNotPlayingSessions = true;
+ for (int userId : mCurrentUserIdList) {
+ UserRecord ur = mUserRecords.get(userId);
+ if (ur.mLastMediaButtonReceiver != null
+ || ur.mRestoredMediaButtonReceiver != null) {
+ useNotPlayingSessions = false;
+ break;
+ }
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "dispatchMediaKeyEvent, useNotPlayingSessions="
+ + useNotPlayingSessions);
+ }
+ MediaSessionRecord session = mPriorityStack.getDefaultMediaButtonSession(
+ mCurrentUserIdList, useNotPlayingSessions);
if (isVoiceKey(keyEvent.getKeyCode())) {
handleVoiceKeyEventLocked(keyEvent, needWakeLock, session);
} else {
@@ -786,13 +798,11 @@
@Override
public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
- final int pid = Binder.getCallingPid();
- final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
MediaSessionRecord session = mPriorityStack
- .getDefaultVolumeSession(mCurrentUserId);
+ .getDefaultVolumeSession(mCurrentUserIdList);
dispatchAdjustVolumeLocked(suggestedStream, delta, flags, session);
}
} finally {
@@ -899,7 +909,7 @@
}
} else {
session.adjustVolume(direction, flags, getContext().getPackageName(),
- UserHandle.myUserId(), true);
+ Process.SYSTEM_UID, true);
}
}
@@ -946,13 +956,16 @@
// won't release it later
session.sendMediaButton(keyEvent,
needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
- mKeyEventReceiver, getContext().getApplicationInfo().uid,
+ mKeyEventReceiver, Process.SYSTEM_UID,
getContext().getPackageName());
} else {
// Launch the last PendingIntent we had with priority
- UserRecord user = mUserRecords.get(mCurrentUserId);
- if (user != null && (user.mLastMediaButtonReceiver != null
- || user.mRestoredMediaButtonReceiver != null)) {
+ for (int userId : mCurrentUserIdList) {
+ UserRecord user = mUserRecords.get(userId);
+ if (user.mLastMediaButtonReceiver == null
+ && user.mRestoredMediaButtonReceiver == null) {
+ continue;
+ }
if (DEBUG) {
Log.d(TAG, "Sending media key to last known PendingIntent "
+ user.mLastMediaButtonReceiver + " or restored Intent "
@@ -972,30 +985,30 @@
} else {
mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver);
getContext().sendBroadcastAsUser(mediaButtonIntent,
- new UserHandle(mCurrentUserId));
+ UserHandle.of(userId));
}
} catch (CanceledException e) {
Log.i(TAG, "Error sending key event to media button receiver "
+ user.mLastMediaButtonReceiver, e);
}
- } else {
- if (DEBUG) {
- Log.d(TAG, "Sending media key ordered broadcast");
- }
- if (needWakeLock) {
- mMediaEventWakeLock.acquire();
- }
- // Fallback to legacy behavior
- Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
- keyIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
- if (needWakeLock) {
- keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED,
- WAKELOCK_RELEASE_ON_FINISHED);
- }
- getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.CURRENT,
- null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
+ return;
}
+ if (DEBUG) {
+ Log.d(TAG, "Sending media key ordered broadcast");
+ }
+ if (needWakeLock) {
+ mMediaEventWakeLock.acquire();
+ }
+ // Fallback to legacy behavior
+ Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
+ keyIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+ if (needWakeLock) {
+ keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
+ }
+ // Send broadcast only to the full user.
+ getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.CURRENT,
+ null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
}
}
@@ -1025,6 +1038,7 @@
if (voiceIntent != null) {
voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
}
} catch (ActivityNotFoundException e) {
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index cc007ef..3327b36 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -32,7 +32,7 @@
* Keeps track of media sessions and their priority for notifications, media
* button dispatch, etc.
*/
-public class MediaSessionStack {
+class MediaSessionStack {
/**
* These are states that usually indicate the user took an action and should
* bump priority regardless of the old state.
@@ -68,13 +68,10 @@
* Checks if a media session is created from the most recent app.
*
* @param record A media session record to be examined.
- * @return true if the media session's package name equals to the most recent app, false
- * otherwise.
+ * @return {@code true} if the media session's package name equals to the most recent app, false
+ * otherwise.
*/
private static boolean isFromMostRecentApp(MediaSessionRecord record) {
- if (ActivityManager.getCurrentUser() != record.getUserId()) {
- return false;
- }
try {
List<ActivityManager.RecentTaskInfo> tasks =
ActivityManagerNative.getDefault().getRecentTasks(1,
@@ -84,9 +81,10 @@
ActivityManager.RECENT_WITH_EXCLUDED, record.getUserId()).getList();
if (tasks != null && !tasks.isEmpty()) {
ActivityManager.RecentTaskInfo recentTask = tasks.get(0);
- if (recentTask.baseIntent != null)
+ if (recentTask.userId == record.getUserId() && recentTask.baseIntent != null) {
return recentTask.baseIntent.getComponent().getPackageName()
.equals(record.getPackageName());
+ }
}
} catch (RemoteException e) {
return false;
@@ -98,11 +96,12 @@
* Add a record to the priority tracker.
*
* @param record The record to add.
+ * @param fromForegroundUser {@code true} if the session is created by the foreground user.
*/
- public void addSession(MediaSessionRecord record) {
+ public void addSession(MediaSessionRecord record, boolean fromForegroundUser) {
mSessions.add(record);
clearCache();
- if (isFromMostRecentApp(record)) {
+ if (fromForegroundUser && isFromMostRecentApp(record)) {
mLastInterestingRecord = record;
}
}
@@ -210,12 +209,13 @@
/**
* Get the highest priority session that can handle media buttons.
*
- * @param userId The user to check.
+ * @param userIdList The user lists to check.
* @param includeNotPlaying Return a non-playing session if nothing else is
* available
* @return The default media button session or null.
*/
- public MediaSessionRecord getDefaultMediaButtonSession(int userId, boolean includeNotPlaying) {
+ public MediaSessionRecord getDefaultMediaButtonSession(
+ List<Integer> userIdList, boolean includeNotPlaying) {
if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) {
return mGlobalPrioritySession;
}
@@ -223,7 +223,7 @@
return mCachedButtonReceiver;
}
ArrayList<MediaSessionRecord> records = getPriorityListLocked(true,
- MediaSession.FLAG_HANDLES_MEDIA_BUTTONS, userId);
+ MediaSession.FLAG_HANDLES_MEDIA_BUTTONS, userIdList);
if (records.size() > 0) {
MediaSessionRecord record = records.get(0);
if (record.isPlaybackActive(false)) {
@@ -248,14 +248,14 @@
return mCachedButtonReceiver;
}
- public MediaSessionRecord getDefaultVolumeSession(int userId) {
+ public MediaSessionRecord getDefaultVolumeSession(List<Integer> userIdList) {
if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) {
return mGlobalPrioritySession;
}
if (mCachedVolumeDefault != null) {
return mCachedVolumeDefault;
}
- ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId);
+ ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userIdList);
int size = records.size();
for (int i = 0; i < size; i++) {
MediaSessionRecord record = records.get(i);
@@ -298,6 +298,13 @@
}
}
+ private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags,
+ int userId) {
+ List<Integer> userIdList = new ArrayList<>();
+ userIdList.add(userId);
+ return getPriorityListLocked(activeOnly, withFlags, userIdList);
+ }
+
/**
* Get a priority sorted list of sessions. Can filter to only return active
* sessions or sessions with specific flags.
@@ -306,22 +313,23 @@
* all sessions.
* @param withFlags Only return sessions with all the specified flags set. 0
* returns all sessions.
- * @param userId The user to get sessions for. {@link UserHandle#USER_ALL}
+ * @param userIdList The user to get sessions for. {@link UserHandle#USER_ALL}
* will return sessions for all users.
* @return The priority sorted list of sessions.
*/
private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags,
- int userId) {
+ List<Integer> userIdList) {
ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>();
int lastLocalIndex = 0;
int lastActiveIndex = 0;
int lastPublishedIndex = 0;
+ boolean filterUser = !userIdList.contains(UserHandle.USER_ALL);
int size = mSessions.size();
for (int i = 0; i < size; i++) {
final MediaSessionRecord session = mSessions.get(i);
- if (userId != UserHandle.USER_ALL && userId != session.getUserId()) {
+ if (filterUser && !userIdList.contains(session.getUserId())) {
// Filter out sessions for the wrong user
continue;
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 228c015..f3fc676 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -89,6 +89,7 @@
import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.Manifest;
+import android.annotation.IntDef;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -132,7 +133,6 @@
import android.os.HandlerThread;
import android.os.IDeviceIdleController;
import android.os.INetworkManagementService;
-import android.os.IPowerManager;
import android.os.Message;
import android.os.MessageQueue.IdleHandler;
import android.os.PowerManager;
@@ -162,6 +162,7 @@
import android.util.Xml;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.ArrayUtils;
@@ -187,6 +188,8 @@
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.util.ArrayList;
import java.util.Arrays;
@@ -199,6 +202,26 @@
* Derives active rules by combining a given policy with other system status,
* and delivers to listeners, such as {@link ConnectivityManager}, for
* enforcement.
+ *
+ * <p>
+ * This class uses 2-3 locks to synchronize state:
+ * <ul>
+ * <li>{@code mUidRulesFirstLock}: used to guard state related to individual UIDs (such as firewall
+ * rules).
+ * <li>{@code mNetworkPoliciesSecondLock}: used to guard state related to network interfaces (such
+ * as network policies).
+ * <li>{@code allLocks}: not a "real" lock, but an indication (through @GuardedBy) that all locks
+ * must be held.
+ * </ul>
+ *
+ * <p>
+ * As such, methods that require synchronization have the following prefixes:
+ * <ul>
+ * <li>{@code UL()}: require the "UID" lock ({@code mUidRulesFirstLock}).
+ * <li>{@code NL()}: require the "Network" lock ({@code mNetworkPoliciesSecondLock}).
+ * <li>{@code AL()}: require all locks, which must be obtained in order ({@code mUidRulesFirstLock}
+ * first, then {@code mNetworkPoliciesSecondLock}, then {@code mYetAnotherGuardThirdLock}, etc..
+ * </ul>
*/
public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
static final String TAG = "NetworkPolicy";
@@ -262,7 +285,6 @@
private static final int MSG_LIMIT_REACHED = 5;
private static final int MSG_RESTRICT_BACKGROUND_CHANGED = 6;
private static final int MSG_ADVISE_PERSIST_THRESHOLD = 7;
- private static final int MSG_SCREEN_ON_CHANGED = 8;
private static final int MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED = 9;
private static final int MSG_UPDATE_INTERFACE_QUOTA = 10;
private static final int MSG_REMOVE_INTERFACE_QUOTA = 11;
@@ -270,7 +292,6 @@
private final Context mContext;
private final IActivityManager mActivityManager;
- private final IPowerManager mPowerManager;
private final INetworkStatsService mNetworkStats;
private final INetworkManagementService mNetworkManager;
private UsageStatsManagerInternal mUsageStats;
@@ -282,13 +303,15 @@
private PowerManagerInternal mPowerManagerInternal;
private IDeviceIdleController mDeviceIdleController;
- final Object mRulesLock = new Object();
+ // See main javadoc for instructions on how to use these locks.
+ final Object mUidRulesFirstLock = new Object();
+ final Object mNetworkPoliciesSecondLock = new Object();
- volatile boolean mSystemReady;
- volatile boolean mScreenOn;
- volatile boolean mRestrictBackground;
- volatile boolean mRestrictPower;
- volatile boolean mDeviceIdleMode;
+ @GuardedBy("allLocks") volatile boolean mSystemReady;
+
+ @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictBackground;
+ @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictPower;
+ @GuardedBy("mUidRulesFirstLock") volatile boolean mDeviceIdleMode;
private final boolean mSuppressDefaultPolicy;
@@ -298,15 +321,19 @@
final ArrayMap<NetworkPolicy, String[]> mNetworkRules = new ArrayMap<>();
/** Defined UID policies. */
- final SparseIntArray mUidPolicy = new SparseIntArray();
+ @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidPolicy = new SparseIntArray();
/** Currently derived rules for each UID. */
- final SparseIntArray mUidRules = new SparseIntArray();
+ @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidRules = new SparseIntArray();
+ @GuardedBy("mUidRulesFirstLock")
final SparseIntArray mUidFirewallStandbyRules = new SparseIntArray();
+ @GuardedBy("mUidRulesFirstLock")
final SparseIntArray mUidFirewallDozableRules = new SparseIntArray();
+ @GuardedBy("mUidRulesFirstLock")
final SparseIntArray mUidFirewallPowerSaveRules = new SparseIntArray();
/** Set of states for the child firewall chains. True if the chain is active. */
+ @GuardedBy("mUidRulesFirstLock")
final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
/**
@@ -314,6 +341,7 @@
* in power save mode, except device idle (doze) still applies.
* TODO: An int array might be sufficient
*/
+ @GuardedBy("mUidRulesFirstLock")
private final SparseBooleanArray mPowerSaveWhitelistExceptIdleAppIds = new SparseBooleanArray();
/**
@@ -321,18 +349,22 @@
* in power save mode.
* TODO: An int array might be sufficient
*/
+ @GuardedBy("mUidRulesFirstLock")
private final SparseBooleanArray mPowerSaveWhitelistAppIds = new SparseBooleanArray();
+ @GuardedBy("mUidRulesFirstLock")
private final SparseBooleanArray mPowerSaveTempWhitelistAppIds = new SparseBooleanArray();
/**
* UIDs that have been white-listed to avoid restricted background.
*/
+ @GuardedBy("mUidRulesFirstLock")
private final SparseBooleanArray mRestrictBackgroundWhitelistUids = new SparseBooleanArray();
/**
* UIDs that have been initially white-listed by system to avoid restricted background.
*/
+ @GuardedBy("mUidRulesFirstLock")
private final SparseBooleanArray mDefaultRestrictBackgroundWhitelistUids =
new SparseBooleanArray();
@@ -340,18 +372,23 @@
* UIDs that have been initially white-listed by system to avoid restricted background,
* but later revoked by user.
*/
+ @GuardedBy("mUidRulesFirstLock")
private final SparseBooleanArray mRestrictBackgroundWhitelistRevokedUids =
new SparseBooleanArray();
/** Set of ifaces that are metered. */
+ @GuardedBy("mNetworkPoliciesSecondLock")
private ArraySet<String> mMeteredIfaces = new ArraySet<>();
/** Set of over-limit templates that have been notified. */
+ @GuardedBy("mNetworkPoliciesSecondLock")
private final ArraySet<NetworkTemplate> mOverLimitNotified = new ArraySet<>();
/** Set of currently active {@link Notification} tags. */
+ @GuardedBy("mNetworkPoliciesSecondLock")
private final ArraySet<String> mActiveNotifs = new ArraySet<String>();
/** Foreground at UID granularity. */
+ @GuardedBy("mUidRulesFirstLock")
final SparseIntArray mUidState = new SparseIntArray();
/** Higher priority listener before general event dispatch */
@@ -362,6 +399,7 @@
final Handler mHandler;
+ @GuardedBy("allLocks")
private final AtomicFile mPolicyFile;
private final AppOpsManager mAppOps;
@@ -376,9 +414,8 @@
// TODO: migrate notifications to SystemUI
public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
- IPowerManager powerManager, INetworkStatsService networkStats,
- INetworkManagementService networkManagement) {
- this(context, activityManager, powerManager, networkStats, networkManagement,
+ INetworkStatsService networkStats, INetworkManagementService networkManagement) {
+ this(context, activityManager, networkStats, networkManagement,
NtpTrustedTime.getInstance(context), getSystemDir(), false);
}
@@ -387,12 +424,10 @@
}
public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
- IPowerManager powerManager, INetworkStatsService networkStats,
- INetworkManagementService networkManagement, TrustedTime time, File systemDir,
- boolean suppressDefaultPolicy) {
+ INetworkStatsService networkStats, INetworkManagementService networkManagement,
+ TrustedTime time, File systemDir, boolean suppressDefaultPolicy) {
mContext = checkNotNull(context, "missing context");
mActivityManager = checkNotNull(activityManager, "missing activityManager");
- mPowerManager = checkNotNull(powerManager, "missing powerManager");
mNetworkStats = checkNotNull(networkStats, "missing networkStats");
mNetworkManager = checkNotNull(networkManagement, "missing networkManagement");
mDeviceIdleController = IDeviceIdleController.Stub.asInterface(ServiceManager.getService(
@@ -426,7 +461,7 @@
mNotifManager = checkNotNull(notifManager, "missing INotificationManager");
}
- void updatePowerSaveWhitelistLocked() {
+ void updatePowerSaveWhitelistUL() {
try {
int[] whitelist = mDeviceIdleController.getAppIdWhitelistExceptIdle();
mPowerSaveWhitelistExceptIdleAppIds.clear();
@@ -452,19 +487,19 @@
*
* @return whether any uid has been added to {@link #mRestrictBackgroundWhitelistUids}.
*/
- boolean addDefaultRestrictBackgroundWhitelistUidsLocked() {
+ boolean addDefaultRestrictBackgroundWhitelistUidsUL() {
final List<UserInfo> users = mUserManager.getUsers();
final int numberUsers = users.size();
boolean changed = false;
for (int i = 0; i < numberUsers; i++) {
final UserInfo user = users.get(i);
- changed = addDefaultRestrictBackgroundWhitelistUidsLocked(user.id) || changed;
+ changed = addDefaultRestrictBackgroundWhitelistUidsUL(user.id) || changed;
}
return changed;
}
- private boolean addDefaultRestrictBackgroundWhitelistUidsLocked(int userId) {
+ private boolean addDefaultRestrictBackgroundWhitelistUidsUL(int userId) {
final SystemConfig sysConfig = SystemConfig.getInstance();
final PackageManager pm = mContext.getPackageManager();
final ArraySet<String> allowDataUsage = sysConfig.getAllowInDataUsageSave();
@@ -502,7 +537,7 @@
return changed;
}
- void updatePowerSaveTempWhitelistLocked() {
+ void updatePowerSaveTempWhitelistUL() {
try {
// Clear the states of the current whitelist
final int N = mPowerSaveTempWhitelistAppIds.size();
@@ -523,7 +558,7 @@
/**
* Remove unnecessary entries in the temp whitelist
*/
- void purgePowerSaveTempWhitelistLocked() {
+ void purgePowerSaveTempWhitelistUL() {
final int N = mPowerSaveTempWhitelistAppIds.size();
for (int i = N - 1; i >= 0; i--) {
if (mPowerSaveTempWhitelistAppIds.valueAt(i) == false) {
@@ -542,40 +577,40 @@
mPackageMonitor.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
- synchronized (mRulesLock) {
- updatePowerSaveWhitelistLocked();
- mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
- mPowerManagerInternal.registerLowPowerModeObserver(
- new PowerManagerInternal.LowPowerModeListener() {
- @Override
- public void onLowPowerModeChanged(boolean enabled) {
- if (LOGD) Slog.d(TAG, "onLowPowerModeChanged(" + enabled + ")");
- synchronized (mRulesLock) {
- if (mRestrictPower != enabled) {
- mRestrictPower = enabled;
- updateRulesForRestrictPowerLocked();
- updateRulesForGlobalChangeLocked(true);
+ synchronized (mUidRulesFirstLock) {
+ synchronized (mNetworkPoliciesSecondLock) {
+ updatePowerSaveWhitelistUL();
+ mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+ mPowerManagerInternal.registerLowPowerModeObserver(
+ new PowerManagerInternal.LowPowerModeListener() {
+ @Override
+ public void onLowPowerModeChanged(boolean enabled) {
+ if (LOGD) Slog.d(TAG, "onLowPowerModeChanged(" + enabled + ")");
+ synchronized (mUidRulesFirstLock) {
+ if (mRestrictPower != enabled) {
+ mRestrictPower = enabled;
+ updateRulesForRestrictPowerUL();
+ }
}
}
+ });
+ mRestrictPower = mPowerManagerInternal.getLowPowerModeEnabled();
+
+ mSystemReady = true;
+
+ // read policy from disk
+ readPolicyAL();
+
+ if (addDefaultRestrictBackgroundWhitelistUidsUL()) {
+ writePolicyAL();
}
- });
- mRestrictPower = mPowerManagerInternal.getLowPowerModeEnabled();
- mSystemReady = true;
-
- // read policy from disk
- readPolicyLocked();
-
- if (addDefaultRestrictBackgroundWhitelistUidsLocked()) {
- writePolicyLocked();
+ setRestrictBackgroundUL(mRestrictBackground);
+ updateRulesForGlobalChangeAL(false);
+ updateNotificationsNL();
}
-
- updateRulesForGlobalChangeLocked(false);
- updateNotificationsLocked();
}
- updateScreenOn();
-
try {
mActivityManager.registerUidObserver(mUidObserver,
ActivityManager.UID_OBSERVER_PROCSTATE|ActivityManager.UID_OBSERVER_GONE);
@@ -584,14 +619,6 @@
// ignored; both services live in system_server
}
- // TODO: traverse existing processes to know foreground state, or have
- // activitymanager dispatch current state when new observer attached.
-
- final IntentFilter screenFilter = new IntentFilter();
- screenFilter.addAction(Intent.ACTION_SCREEN_ON);
- screenFilter.addAction(Intent.ACTION_SCREEN_OFF);
- mContext.registerReceiver(mScreenReceiver, screenFilter);
-
// listen for changes to power save whitelist
final IntentFilter whitelistFilter = new IntentFilter(
PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
@@ -650,14 +677,14 @@
final private IUidObserver mUidObserver = new IUidObserver.Stub() {
@Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
- synchronized (mRulesLock) {
- updateUidStateLocked(uid, procState);
+ synchronized (mUidRulesFirstLock) {
+ updateUidStateUL(uid, procState);
}
}
@Override public void onUidGone(int uid) throws RemoteException {
- synchronized (mRulesLock) {
- removeUidStateLocked(uid);
+ synchronized (mUidRulesFirstLock) {
+ removeUidStateUL(uid);
}
}
@@ -672,9 +699,9 @@
@Override
public void onReceive(Context context, Intent intent) {
// on background handler thread, and POWER_SAVE_WHITELIST_CHANGED is protected
- synchronized (mRulesLock) {
- updatePowerSaveWhitelistLocked();
- updateRulesForGlobalChangeLocked(false);
+ synchronized (mUidRulesFirstLock) {
+ updatePowerSaveWhitelistUL();
+ updateRulesForRestrictPowerUL();
}
}
};
@@ -682,23 +709,14 @@
final private Runnable mTempPowerSaveChangedCallback = new Runnable() {
@Override
public void run() {
- synchronized (mRulesLock) {
- updatePowerSaveTempWhitelistLocked();
- updateRulesForTempWhitelistChangeLocked();
- purgePowerSaveTempWhitelistLocked();
+ synchronized (mUidRulesFirstLock) {
+ updatePowerSaveTempWhitelistUL();
+ updateRulesForTempWhitelistChangeUL();
+ purgePowerSaveTempWhitelistUL();
}
}
};
- final private BroadcastReceiver mScreenReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // screen-related broadcasts are protected by system, no need
- // for permissions check.
- mHandler.obtainMessage(MSG_SCREEN_ON_CHANGED).sendToTarget();
- }
- };
-
final private BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -712,8 +730,8 @@
// update rules for UID, since it might be subject to
// global background data policy
if (LOGV) Slog.v(TAG, "ACTION_PACKAGE_ADDED for uid=" + uid);
- synchronized (mRulesLock) {
- updateRestrictionRulesForUidLocked(uid);
+ synchronized (mUidRulesFirstLock) {
+ updateRestrictionRulesForUidUL(uid);
}
}
}
@@ -729,10 +747,12 @@
// remove any policy and update rules to clean up
if (LOGV) Slog.v(TAG, "ACTION_UID_REMOVED for uid=" + uid);
- synchronized (mRulesLock) {
+ synchronized (mUidRulesFirstLock) {
mUidPolicy.delete(uid);
- updateRestrictionRulesForUidLocked(uid);
- writePolicyLocked();
+ updateRestrictionRulesForUidUL(uid);
+ synchronized (mNetworkPoliciesSecondLock) {
+ writePolicyAL();
+ }
}
}
};
@@ -750,16 +770,18 @@
switch (action) {
case ACTION_USER_REMOVED:
case ACTION_USER_ADDED:
- synchronized (mRulesLock) {
+ synchronized (mUidRulesFirstLock) {
// Remove any persistable state for the given user; both cleaning up after a
// USER_REMOVED, and one last sanity check during USER_ADDED
- removeUserStateLocked(userId, true);
+ removeUserStateUL(userId, true);
if (action == ACTION_USER_ADDED) {
// Add apps that are whitelisted by default.
- addDefaultRestrictBackgroundWhitelistUidsLocked(userId);
+ addDefaultRestrictBackgroundWhitelistUidsUL(userId);
}
// Update global restrict for that user
- updateRulesForGlobalChangeLocked(true);
+ synchronized (mNetworkPoliciesSecondLock) {
+ updateRulesForGlobalChangeAL(true);
+ }
}
break;
}
@@ -777,9 +799,9 @@
// READ_NETWORK_USAGE_HISTORY permission above.
maybeRefreshTrustedTime();
- synchronized (mRulesLock) {
- updateNetworkEnabledLocked();
- updateNotificationsLocked();
+ synchronized (mNetworkPoliciesSecondLock) {
+ updateNetworkEnabledNL();
+ updateNotificationsNL();
}
}
};
@@ -828,10 +850,12 @@
EXTRA_WIFI_CONFIGURATION);
if (config.SSID != null) {
final NetworkTemplate template = NetworkTemplate.buildTemplateWifi(config.SSID);
- synchronized (mRulesLock) {
- if (mNetworkPolicy.containsKey(template)) {
- mNetworkPolicy.remove(template);
- writePolicyLocked();
+ synchronized (mUidRulesFirstLock) {
+ synchronized (mNetworkPoliciesSecondLock) {
+ if (mNetworkPolicy.containsKey(template)) {
+ mNetworkPolicy.remove(template);
+ writePolicyAL();
+ }
}
}
}
@@ -857,13 +881,13 @@
final boolean meteredHint = info.getMeteredHint();
final NetworkTemplate template = NetworkTemplate.buildTemplateWifi(info.getSSID());
- synchronized (mRulesLock) {
+ synchronized (mNetworkPoliciesSecondLock) {
NetworkPolicy policy = mNetworkPolicy.get(template);
if (policy == null && meteredHint) {
// policy doesn't exist, and AP is hinting that it's
// metered: create an inferred policy.
policy = newWifiPolicy(template, meteredHint);
- addNetworkPolicyLocked(policy);
+ addNetworkPolicyNL(policy);
} else if (policy != null && policy.inferred) {
// policy exists, and was inferred: update its current
@@ -872,7 +896,7 @@
// since this is inferred for each wifi session, just update
// rules without persisting.
- updateNetworkRulesLocked();
+ updateNetworkRulesNL();
}
}
}
@@ -904,8 +928,8 @@
* Check {@link NetworkPolicy} against current {@link INetworkStatsService}
* to show visible notifications as needed.
*/
- void updateNotificationsLocked() {
- if (LOGV) Slog.v(TAG, "updateNotificationsLocked()");
+ void updateNotificationsNL() {
+ if (LOGV) Slog.v(TAG, "updateNotificationsNL()");
// keep track of previously active notifications
final ArraySet<String> beforeNotifs = new ArraySet<String>(mActiveNotifs);
@@ -931,11 +955,11 @@
enqueueNotification(policy, TYPE_LIMIT_SNOOZED, totalBytes);
} else {
enqueueNotification(policy, TYPE_LIMIT, totalBytes);
- notifyOverLimitLocked(policy.template);
+ notifyOverLimitNL(policy.template);
}
} else {
- notifyUnderLimitLocked(policy.template);
+ notifyUnderLimitNL(policy.template);
if (policy.isOverWarning(totalBytes) && policy.lastWarningSnooze < start) {
enqueueNotification(policy, TYPE_WARNING, totalBytes);
@@ -983,14 +1007,14 @@
* Notify that given {@link NetworkTemplate} is over
* {@link NetworkPolicy#limitBytes}, potentially showing dialog to user.
*/
- private void notifyOverLimitLocked(NetworkTemplate template) {
+ private void notifyOverLimitNL(NetworkTemplate template) {
if (!mOverLimitNotified.contains(template)) {
mContext.startActivity(buildNetworkOverLimitIntent(template));
mOverLimitNotified.add(template);
}
}
- private void notifyUnderLimitLocked(NetworkTemplate template) {
+ private void notifyUnderLimitNL(NetworkTemplate template) {
mOverLimitNotified.remove(template);
}
@@ -1024,6 +1048,8 @@
builder.setTicker(title);
builder.setContentTitle(title);
builder.setContentText(body);
+ builder.setDefaults(Notification.DEFAULT_ALL);
+ builder.setPriority(Notification.PRIORITY_HIGH);
final Intent snoozeIntent = buildSnoozeWarningIntent(policy.template);
builder.setDeleteIntent(PendingIntent.getBroadcast(
@@ -1142,12 +1168,12 @@
// permission above.
maybeRefreshTrustedTime();
- synchronized (mRulesLock) {
- ensureActiveMobilePolicyLocked();
- normalizePoliciesLocked();
- updateNetworkEnabledLocked();
- updateNetworkRulesLocked();
- updateNotificationsLocked();
+ synchronized (mNetworkPoliciesSecondLock) {
+ ensureActiveMobilePolicyNL();
+ normalizePoliciesNL();
+ updateNetworkEnabledNL();
+ updateNetworkRulesNL();
+ updateNotificationsNL();
}
}
};
@@ -1156,8 +1182,8 @@
* Proactively control network data connections when they exceed
* {@link NetworkPolicy#limitBytes}.
*/
- void updateNetworkEnabledLocked() {
- if (LOGV) Slog.v(TAG, "updateNetworkEnabledLocked()");
+ void updateNetworkEnabledNL() {
+ if (LOGV) Slog.v(TAG, "updateNetworkEnabledNL()");
// TODO: reset any policy-disabled networks when any policy is removed
// completely, which is currently rare case.
@@ -1191,6 +1217,24 @@
private void setNetworkTemplateEnabled(NetworkTemplate template, boolean enabled) {
// TODO: reach into ConnectivityManager to proactively disable bringing
// up this network, since we know that traffic will be blocked.
+
+ if (template.getMatchRule() == MATCH_MOBILE_ALL) {
+ // If mobile data usage hits the limit or if the user resumes the data, we need to
+ // notify telephony.
+ final SubscriptionManager sm = SubscriptionManager.from(mContext);
+ final TelephonyManager tm = TelephonyManager.from(mContext);
+
+ final int[] subIds = sm.getActiveSubscriptionIdList();
+ for (int subId : subIds) {
+ final String subscriberId = tm.getSubscriberId(subId);
+ final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true);
+ // Template is matched when subscriber id matches.
+ if (template.matches(probeIdent)) {
+ tm.setPolicyDataEnabled(enabled, subId);
+ }
+ }
+ }
}
/**
@@ -1198,8 +1242,8 @@
* {@link NetworkPolicy} that need to be enforced. When matches found, set
* remaining quota based on usage cycle and historical stats.
*/
- void updateNetworkRulesLocked() {
- if (LOGV) Slog.v(TAG, "updateNetworkRulesLocked()");
+ void updateNetworkRulesNL() {
+ if (LOGV) Slog.v(TAG, "updateNetworkRulesNL()");
final NetworkState[] states;
try {
@@ -1349,8 +1393,8 @@
* Once any {@link #mNetworkPolicy} are loaded from disk, ensure that we
* have at least a default mobile policy defined.
*/
- private void ensureActiveMobilePolicyLocked() {
- if (LOGV) Slog.v(TAG, "ensureActiveMobilePolicyLocked()");
+ private void ensureActiveMobilePolicyNL() {
+ if (LOGV) Slog.v(TAG, "ensureActiveMobilePolicyNL()");
if (mSuppressDefaultPolicy) return;
final TelephonyManager tele = TelephonyManager.from(mContext);
@@ -1359,11 +1403,11 @@
final int[] subIds = sub.getActiveSubscriptionIdList();
for (int subId : subIds) {
final String subscriberId = tele.getSubscriberId(subId);
- ensureActiveMobilePolicyLocked(subscriberId);
+ ensureActiveMobilePolicyNL(subscriberId);
}
}
- private void ensureActiveMobilePolicyLocked(String subscriberId) {
+ private void ensureActiveMobilePolicyNL(String subscriberId) {
// Poke around to see if we already have a policy
final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true);
@@ -1394,11 +1438,11 @@
final NetworkTemplate template = buildTemplateMobileAll(subscriberId);
final NetworkPolicy policy = new NetworkPolicy(template, cycleDay, cycleTimezone,
warningBytes, LIMIT_DISABLED, SNOOZE_NEVER, SNOOZE_NEVER, true, true);
- addNetworkPolicyLocked(policy);
+ addNetworkPolicyNL(policy);
}
- private void readPolicyLocked() {
- if (LOGV) Slog.v(TAG, "readPolicyLocked()");
+ private void readPolicyAL() {
+ if (LOGV) Slog.v(TAG, "readPolicyAL()");
// clear any existing policy and read from disk
mNetworkPolicy.clear();
@@ -1498,7 +1542,7 @@
final int policy = readIntAttribute(in, ATTR_POLICY);
if (UserHandle.isApp(uid)) {
- setUidPolicyUncheckedLocked(uid, policy, false);
+ setUidPolicyUncheckedUL(uid, policy, false);
} else {
Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring");
}
@@ -1510,7 +1554,7 @@
// app policy is deprecated so this is only used in pre system user split.
final int uid = UserHandle.getUid(UserHandle.USER_SYSTEM, appId);
if (UserHandle.isApp(uid)) {
- setUidPolicyUncheckedLocked(uid, policy, false);
+ setUidPolicyUncheckedUL(uid, policy, false);
} else {
Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring");
}
@@ -1533,7 +1577,7 @@
} catch (FileNotFoundException e) {
// missing policy is okay, probably first boot
- upgradeLegacyBackgroundData();
+ upgradeLegacyBackgroundDataUL();
} catch (IOException e) {
Log.wtf(TAG, "problem reading network policy", e);
} catch (XmlPullParserException e) {
@@ -1547,7 +1591,7 @@
* Upgrade legacy background data flags, notifying listeners of one last
* change to always-true.
*/
- private void upgradeLegacyBackgroundData() {
+ private void upgradeLegacyBackgroundDataUL() {
mRestrictBackground = Settings.Secure.getInt(
mContext.getContentResolver(), Settings.Secure.BACKGROUND_DATA, 1) != 1;
@@ -1559,8 +1603,8 @@
}
}
- void writePolicyLocked() {
- if (LOGV) Slog.v(TAG, "writePolicyLocked()");
+ void writePolicyAL() {
+ if (LOGV) Slog.v(TAG, "writePolicyAL()");
FileOutputStream fos = null;
try {
@@ -1657,13 +1701,12 @@
if (!UserHandle.isApp(uid)) {
throw new IllegalArgumentException("cannot apply policy to UID " + uid);
}
-
- synchronized (mRulesLock) {
+ synchronized (mUidRulesFirstLock) {
final long token = Binder.clearCallingIdentity();
try {
final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
if (oldPolicy != policy) {
- setUidPolicyUncheckedLocked(uid, oldPolicy, policy, true);
+ setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -1679,11 +1722,11 @@
throw new IllegalArgumentException("cannot apply policy to UID " + uid);
}
- synchronized (mRulesLock) {
+ synchronized (mUidRulesFirstLock) {
final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
policy |= oldPolicy;
if (oldPolicy != policy) {
- setUidPolicyUncheckedLocked(uid, oldPolicy, policy, true);
+ setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
}
}
}
@@ -1696,17 +1739,17 @@
throw new IllegalArgumentException("cannot apply policy to UID " + uid);
}
- synchronized (mRulesLock) {
+ synchronized (mUidRulesFirstLock) {
final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
policy = oldPolicy & ~policy;
if (oldPolicy != policy) {
- setUidPolicyUncheckedLocked(uid, oldPolicy, policy, true);
+ setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
}
}
}
- private void setUidPolicyUncheckedLocked(int uid, int oldPolicy, int policy, boolean persist) {
- setUidPolicyUncheckedLocked(uid, policy, persist);
+ private void setUidPolicyUncheckedUL(int uid, int oldPolicy, int policy, boolean persist) {
+ setUidPolicyUncheckedUL(uid, policy, persist);
final boolean isBlacklisted = policy == POLICY_REJECT_METERED_BACKGROUND;
mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_BLACKLIST_CHANGED, uid,
@@ -1721,13 +1764,15 @@
}
}
- private void setUidPolicyUncheckedLocked(int uid, int policy, boolean persist) {
+ private void setUidPolicyUncheckedUL(int uid, int policy, boolean persist) {
mUidPolicy.put(uid, policy);
// uid policy changed, recompute rules and persist policy.
- updateRulesForDataUsageRestrictionsLocked(uid);
+ updateRulesForDataUsageRestrictionsUL(uid);
if (persist) {
- writePolicyLocked();
+ synchronized (mNetworkPoliciesSecondLock) {
+ writePolicyAL();
+ }
}
}
@@ -1735,7 +1780,7 @@
public int getUidPolicy(int uid) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- synchronized (mRulesLock) {
+ synchronized (mUidRulesFirstLock) {
return mUidPolicy.get(uid, POLICY_NONE);
}
}
@@ -1745,7 +1790,7 @@
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
int[] uids = new int[0];
- synchronized (mRulesLock) {
+ synchronized (mUidRulesFirstLock) {
for (int i = 0; i < mUidPolicy.size(); i++) {
final int uid = mUidPolicy.keyAt(i);
final int uidPolicy = mUidPolicy.valueAt(i);
@@ -1761,9 +1806,9 @@
* Removes any persistable state associated with given {@link UserHandle}, persisting
* if any changes that are made.
*/
- boolean removeUserStateLocked(int userId, boolean writePolicy) {
+ boolean removeUserStateUL(int userId, boolean writePolicy) {
- if (LOGV) Slog.v(TAG, "removeUserStateLocked()");
+ if (LOGV) Slog.v(TAG, "removeUserStateUL()");
boolean changed = false;
// Remove entries from restricted background UID whitelist
@@ -1777,7 +1822,7 @@
if (wlUids.length > 0) {
for (int uid : wlUids) {
- removeRestrictBackgroundWhitelistedUidLocked(uid, false, false);
+ removeRestrictBackgroundWhitelistedUidUL(uid, false, false);
}
changed = true;
}
@@ -1806,11 +1851,11 @@
}
changed = true;
}
-
- updateRulesForGlobalChangeLocked(true);
-
- if (writePolicy && changed) {
- writePolicyLocked();
+ synchronized (mNetworkPoliciesSecondLock) {
+ updateRulesForGlobalChangeAL(true);
+ if (writePolicy && changed) {
+ writePolicyAL();
+ }
}
return changed;
}
@@ -1845,19 +1890,21 @@
final long token = Binder.clearCallingIdentity();
try {
maybeRefreshTrustedTime();
- synchronized (mRulesLock) {
- normalizePoliciesLocked(policies);
- updateNetworkEnabledLocked();
- updateNetworkRulesLocked();
- updateNotificationsLocked();
- writePolicyLocked();
+ synchronized (mUidRulesFirstLock) {
+ synchronized (mNetworkPoliciesSecondLock) {
+ normalizePoliciesNL(policies);
+ updateNetworkEnabledNL();
+ updateNetworkRulesNL();
+ updateNotificationsNL();
+ writePolicyAL();
+ }
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
- void addNetworkPolicyLocked(NetworkPolicy policy) {
+ void addNetworkPolicyNL(NetworkPolicy policy) {
NetworkPolicy[] policies = getNetworkPolicies(mContext.getOpPackageName());
policies = ArrayUtils.appendElement(NetworkPolicy.class, policies, policy);
setNetworkPolicies(policies);
@@ -1879,7 +1926,7 @@
}
}
- synchronized (mRulesLock) {
+ synchronized (mNetworkPoliciesSecondLock) {
final int size = mNetworkPolicy.size();
final NetworkPolicy[] policies = new NetworkPolicy[size];
for (int i = 0; i < size; i++) {
@@ -1889,11 +1936,11 @@
}
}
- private void normalizePoliciesLocked() {
- normalizePoliciesLocked(getNetworkPolicies(mContext.getOpPackageName()));
+ private void normalizePoliciesNL() {
+ normalizePoliciesNL(getNetworkPolicies(mContext.getOpPackageName()));
}
- private void normalizePoliciesLocked(NetworkPolicy[] policies) {
+ private void normalizePoliciesNL(NetworkPolicy[] policies) {
final TelephonyManager tele = TelephonyManager.from(mContext);
final String[] merged = tele.getMergedSubscriberIds();
@@ -1927,29 +1974,31 @@
void performSnooze(NetworkTemplate template, int type) {
maybeRefreshTrustedTime();
final long currentTime = currentTimeMillis();
- synchronized (mRulesLock) {
- // find and snooze local policy that matches
- final NetworkPolicy policy = mNetworkPolicy.get(template);
- if (policy == null) {
- throw new IllegalArgumentException("unable to find policy for " + template);
- }
+ synchronized (mUidRulesFirstLock) {
+ synchronized (mNetworkPoliciesSecondLock) {
+ // find and snooze local policy that matches
+ final NetworkPolicy policy = mNetworkPolicy.get(template);
+ if (policy == null) {
+ throw new IllegalArgumentException("unable to find policy for " + template);
+ }
- switch (type) {
- case TYPE_WARNING:
- policy.lastWarningSnooze = currentTime;
- break;
- case TYPE_LIMIT:
- policy.lastLimitSnooze = currentTime;
- break;
- default:
- throw new IllegalArgumentException("unexpected type");
- }
+ switch (type) {
+ case TYPE_WARNING:
+ policy.lastWarningSnooze = currentTime;
+ break;
+ case TYPE_LIMIT:
+ policy.lastLimitSnooze = currentTime;
+ break;
+ default:
+ throw new IllegalArgumentException("unexpected type");
+ }
- normalizePoliciesLocked();
- updateNetworkEnabledLocked();
- updateNetworkRulesLocked();
- updateNotificationsLocked();
- writePolicyLocked();
+ normalizePoliciesNL();
+ updateNetworkEnabledNL();
+ updateNetworkRulesNL();
+ updateNotificationsNL();
+ writePolicyAL();
+ }
}
}
@@ -1957,7 +2006,7 @@
public void onTetheringChanged(String iface, boolean tethering) {
// No need to enforce permission because setRestrictBackground() will do it.
if (LOGD) Log.d(TAG, "onTetherStateChanged(" + iface + ", " + tethering + ")");
- synchronized (mRulesLock) {
+ synchronized (mUidRulesFirstLock) {
if (mRestrictBackground && tethering) {
Log.d(TAG, "Tethering on (" + iface +"); disable Data Saver");
setRestrictBackground(false);
@@ -1971,13 +2020,13 @@
final long token = Binder.clearCallingIdentity();
try {
maybeRefreshTrustedTime();
- synchronized (mRulesLock) {
+ synchronized (mUidRulesFirstLock) {
if (restrictBackground == mRestrictBackground) {
// Ideally, UI should never allow this scenario...
Slog.w(TAG, "setRestrictBackground: already " + restrictBackground);
return;
}
- setRestrictBackgroundLocked(restrictBackground);
+ setRestrictBackgroundUL(restrictBackground);
}
} finally {
@@ -1988,27 +2037,29 @@
.sendToTarget();
}
- private void setRestrictBackgroundLocked(boolean restrictBackground) {
- Slog.d(TAG, "setRestrictBackgroundLocked(): " + restrictBackground);
+ private void setRestrictBackgroundUL(boolean restrictBackground) {
+ Slog.d(TAG, "setRestrictBackgroundUL(): " + restrictBackground);
final boolean oldRestrictBackground = mRestrictBackground;
mRestrictBackground = restrictBackground;
// Must whitelist foreground apps before turning data saver mode on.
// TODO: there is no need to iterate through all apps here, just those in the foreground,
// so it could call AM to get the UIDs of such apps, and iterate through them instead.
- updateRulesForRestrictBackgroundLocked();
+ updateRulesForAllAppsUL(TYPE_RESTRICT_BACKGROUND);
try {
if (!mNetworkManager.setDataSaverModeEnabled(mRestrictBackground)) {
Slog.e(TAG, "Could not change Data Saver Mode on NMS to " + mRestrictBackground);
mRestrictBackground = oldRestrictBackground;
// TODO: if it knew the foreground apps (see TODO above), it could call
- // updateRulesForRestrictBackgroundLocked() again to restore state.
+ // updateRulesForRestrictBackgroundUL() again to restore state.
return;
}
} catch (RemoteException e) {
// ignored; service lives in system_server
}
- updateNotificationsLocked();
- writePolicyLocked();
+ synchronized (mNetworkPoliciesSecondLock) {
+ updateNotificationsNL();
+ writePolicyAL();
+ }
}
@Override
@@ -2016,7 +2067,8 @@
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
final boolean oldStatus;
final boolean needFirewallRules;
- synchronized (mRulesLock) {
+ int changed;
+ synchronized (mUidRulesFirstLock) {
oldStatus = mRestrictBackgroundWhitelistUids.get(uid);
if (oldStatus) {
if (LOGD) Slog.d(TAG, "uid " + uid + " is already whitelisted");
@@ -2033,12 +2085,14 @@
}
if (needFirewallRules) {
// Only update firewall rules if necessary...
- updateRulesForDataUsageRestrictionsLocked(uid);
+ updateRulesForDataUsageRestrictionsUL(uid);
}
// ...but always persists the whitelist request.
- writePolicyLocked();
+ synchronized (mNetworkPoliciesSecondLock) {
+ writePolicyAL();
+ }
+ changed = (mRestrictBackground && !oldStatus && needFirewallRules) ? 1 : 0;
}
- int changed = (mRestrictBackground && !oldStatus && needFirewallRules) ? 1 : 0;
mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, changed,
Boolean.TRUE).sendToTarget();
}
@@ -2047,8 +2101,8 @@
public void removeRestrictBackgroundWhitelistedUid(int uid) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
final boolean changed;
- synchronized (mRulesLock) {
- changed = removeRestrictBackgroundWhitelistedUidLocked(uid, false, true);
+ synchronized (mUidRulesFirstLock) {
+ changed = removeRestrictBackgroundWhitelistedUidUL(uid, false, true);
}
mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, changed ? 1 : 0,
Boolean.FALSE).sendToTarget();
@@ -2058,7 +2112,7 @@
* Removes a uid from the restricted background whitelist, returning whether its current
* {@link ConnectivityManager.RestrictBackgroundStatus} changed.
*/
- private boolean removeRestrictBackgroundWhitelistedUidLocked(int uid, boolean uidDeleted,
+ private boolean removeRestrictBackgroundWhitelistedUidUL(int uid, boolean uidDeleted,
boolean updateNow) {
final boolean oldStatus = mRestrictBackgroundWhitelistUids.get(uid);
if (!oldStatus && !uidDeleted) {
@@ -2078,11 +2132,13 @@
}
if (needFirewallRules) {
// Only update firewall rules if necessary...
- updateRulesForDataUsageRestrictionsLocked(uid, uidDeleted);
+ updateRulesForDataUsageRestrictionsUL(uid, uidDeleted);
}
if (updateNow) {
// ...but always persists the whitelist request.
- writePolicyLocked();
+ synchronized (mNetworkPoliciesSecondLock) {
+ writePolicyAL();
+ }
}
// Status only changes if Data Saver is turned on (otherwise it is DISABLED, even if the
// app was whitelisted before).
@@ -2092,7 +2148,7 @@
@Override
public int[] getRestrictBackgroundWhitelistedUids() {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- synchronized (mRulesLock) {
+ synchronized (mUidRulesFirstLock) {
final int size = mRestrictBackgroundWhitelistUids.size();
final int[] whitelist = new int[size];
for (int i = 0; i < size; i++) {
@@ -2111,7 +2167,7 @@
mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG);
final int uid = Binder.getCallingUid();
- synchronized (mRulesLock) {
+ synchronized (mUidRulesFirstLock) {
// Must clear identity because getUidPolicy() is restricted to system.
final long token = Binder.clearCallingIdentity();
final int policy;
@@ -2137,7 +2193,7 @@
public boolean getRestrictBackground() {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- synchronized (mRulesLock) {
+ synchronized (mUidRulesFirstLock) {
return mRestrictBackground;
}
}
@@ -2146,13 +2202,13 @@
public void setDeviceIdleMode(boolean enabled) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- synchronized (mRulesLock) {
+ synchronized (mUidRulesFirstLock) {
if (mDeviceIdleMode != enabled) {
mDeviceIdleMode = enabled;
if (mSystemReady) {
// Device idle change means we need to rebuild rules for all
// known apps, so do a global refresh.
- updateRulesForGlobalChangeLocked(false);
+ updateRulesForRestrictPowerUL();
}
if (enabled) {
EventLogTags.writeDeviceIdleOnPhase("net");
@@ -2163,7 +2219,7 @@
}
}
- private NetworkPolicy findPolicyForNetworkLocked(NetworkIdentity ident) {
+ private NetworkPolicy findPolicyForNetworkNL(NetworkIdentity ident) {
for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
NetworkPolicy policy = mNetworkPolicy.valueAt(i);
if (policy.template.matches(ident)) {
@@ -2191,8 +2247,8 @@
final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
final NetworkPolicy policy;
- synchronized (mRulesLock) {
- policy = findPolicyForNetworkLocked(ident);
+ synchronized (mNetworkPoliciesSecondLock) {
+ policy = findPolicyForNetworkNL(ident);
}
if (policy == null || !policy.hasCycle()) {
@@ -2224,21 +2280,16 @@
final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
- // roaming networks are always considered metered
- if (ident.getRoaming()) {
- return true;
- }
-
final NetworkPolicy policy;
- synchronized (mRulesLock) {
- policy = findPolicyForNetworkLocked(ident);
+ synchronized (mNetworkPoliciesSecondLock) {
+ policy = findPolicyForNetworkNL(ident);
}
if (policy != null) {
return policy.metered;
} else {
final int type = state.networkInfo.getType();
- if (isNetworkTypeMobile(type) || type == TYPE_WIMAX) {
+ if ((isNetworkTypeMobile(type) && ident.getMetered()) || type == TYPE_WIMAX) {
return true;
}
return false;
@@ -2256,155 +2307,157 @@
argSet.add(arg);
}
- synchronized (mRulesLock) {
- if (argSet.contains("--unsnooze")) {
- for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
- mNetworkPolicy.valueAt(i).clearSnooze();
+ synchronized (mUidRulesFirstLock) {
+ synchronized (mNetworkPoliciesSecondLock) {
+ if (argSet.contains("--unsnooze")) {
+ for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
+ mNetworkPolicy.valueAt(i).clearSnooze();
+ }
+
+ normalizePoliciesNL();
+ updateNetworkEnabledNL();
+ updateNetworkRulesNL();
+ updateNotificationsNL();
+ writePolicyAL();
+
+ fout.println("Cleared snooze timestamps");
+ return;
}
- normalizePoliciesLocked();
- updateNetworkEnabledLocked();
- updateNetworkRulesLocked();
- updateNotificationsLocked();
- writePolicyLocked();
-
- fout.println("Cleared snooze timestamps");
- return;
- }
-
- fout.print("System ready: "); fout.println(mSystemReady);
- fout.print("Restrict background: "); fout.println(mRestrictBackground);
- fout.print("Restrict power: "); fout.println(mRestrictPower);
- fout.print("Device idle: "); fout.println(mDeviceIdleMode);
- fout.println("Network policies:");
- fout.increaseIndent();
- for (int i = 0; i < mNetworkPolicy.size(); i++) {
- fout.println(mNetworkPolicy.valueAt(i).toString());
- }
- fout.decreaseIndent();
-
- fout.print("Metered ifaces: "); fout.println(String.valueOf(mMeteredIfaces));
-
- fout.println("Policy for UIDs:");
- fout.increaseIndent();
- int size = mUidPolicy.size();
- for (int i = 0; i < size; i++) {
- final int uid = mUidPolicy.keyAt(i);
- final int policy = mUidPolicy.valueAt(i);
- fout.print("UID=");
- fout.print(uid);
- fout.print(" policy=");
- fout.print(DebugUtils.flagsToString(NetworkPolicyManager.class, "POLICY_", policy));
- fout.println();
- }
- fout.decreaseIndent();
-
- size = mPowerSaveWhitelistExceptIdleAppIds.size();
- if (size > 0) {
- fout.println("Power save whitelist (except idle) app ids:");
+ fout.print("System ready: "); fout.println(mSystemReady);
+ fout.print("Restrict background: "); fout.println(mRestrictBackground);
+ fout.print("Restrict power: "); fout.println(mRestrictPower);
+ fout.print("Device idle: "); fout.println(mDeviceIdleMode);
+ fout.println("Network policies:");
fout.increaseIndent();
+ for (int i = 0; i < mNetworkPolicy.size(); i++) {
+ fout.println(mNetworkPolicy.valueAt(i).toString());
+ }
+ fout.decreaseIndent();
+
+ fout.print("Metered ifaces: "); fout.println(String.valueOf(mMeteredIfaces));
+
+ fout.println("Policy for UIDs:");
+ fout.increaseIndent();
+ int size = mUidPolicy.size();
for (int i = 0; i < size; i++) {
+ final int uid = mUidPolicy.keyAt(i);
+ final int policy = mUidPolicy.valueAt(i);
fout.print("UID=");
- fout.print(mPowerSaveWhitelistExceptIdleAppIds.keyAt(i));
- fout.print(": ");
- fout.print(mPowerSaveWhitelistExceptIdleAppIds.valueAt(i));
+ fout.print(uid);
+ fout.print(" policy=");
+ fout.print(DebugUtils.flagsToString(NetworkPolicyManager.class, "POLICY_", policy));
+ fout.println();
+ }
+ fout.decreaseIndent();
+
+ size = mPowerSaveWhitelistExceptIdleAppIds.size();
+ if (size > 0) {
+ fout.println("Power save whitelist (except idle) app ids:");
+ fout.increaseIndent();
+ for (int i = 0; i < size; i++) {
+ fout.print("UID=");
+ fout.print(mPowerSaveWhitelistExceptIdleAppIds.keyAt(i));
+ fout.print(": ");
+ fout.print(mPowerSaveWhitelistExceptIdleAppIds.valueAt(i));
+ fout.println();
+ }
+ fout.decreaseIndent();
+ }
+
+ size = mPowerSaveWhitelistAppIds.size();
+ if (size > 0) {
+ fout.println("Power save whitelist app ids:");
+ fout.increaseIndent();
+ for (int i = 0; i < size; i++) {
+ fout.print("UID=");
+ fout.print(mPowerSaveWhitelistAppIds.keyAt(i));
+ fout.print(": ");
+ fout.print(mPowerSaveWhitelistAppIds.valueAt(i));
+ fout.println();
+ }
+ fout.decreaseIndent();
+ }
+
+ size = mRestrictBackgroundWhitelistUids.size();
+ if (size > 0) {
+ fout.println("Restrict background whitelist uids:");
+ fout.increaseIndent();
+ for (int i = 0; i < size; i++) {
+ fout.print("UID=");
+ fout.print(mRestrictBackgroundWhitelistUids.keyAt(i));
+ fout.println();
+ }
+ fout.decreaseIndent();
+ }
+
+ size = mDefaultRestrictBackgroundWhitelistUids.size();
+ if (size > 0) {
+ fout.println("Default restrict background whitelist uids:");
+ fout.increaseIndent();
+ for (int i = 0; i < size; i++) {
+ fout.print("UID=");
+ fout.print(mDefaultRestrictBackgroundWhitelistUids.keyAt(i));
+ fout.println();
+ }
+ fout.decreaseIndent();
+ }
+
+ size = mRestrictBackgroundWhitelistRevokedUids.size();
+ if (size > 0) {
+ fout.println("Default restrict background whitelist uids revoked by users:");
+ fout.increaseIndent();
+ for (int i = 0; i < size; i++) {
+ fout.print("UID=");
+ fout.print(mRestrictBackgroundWhitelistRevokedUids.keyAt(i));
+ fout.println();
+ }
+ fout.decreaseIndent();
+ }
+
+ final SparseBooleanArray knownUids = new SparseBooleanArray();
+ collectKeys(mUidState, knownUids);
+ collectKeys(mUidRules, knownUids);
+
+ fout.println("Status for all known UIDs:");
+ fout.increaseIndent();
+ size = knownUids.size();
+ for (int i = 0; i < size; i++) {
+ final int uid = knownUids.keyAt(i);
+ fout.print("UID=");
+ fout.print(uid);
+
+ final int state = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+ fout.print(" state=");
+ fout.print(state);
+ if (state <= ActivityManager.PROCESS_STATE_TOP) {
+ fout.print(" (fg)");
+ } else {
+ fout.print(state <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
+ ? " (fg svc)" : " (bg)");
+ }
+
+ final int uidRules = mUidRules.get(uid, RULE_NONE);
+ fout.print(" rules=");
+ fout.print(uidRulesToString(uidRules));
+ fout.println();
+ }
+ fout.decreaseIndent();
+
+ fout.println("Status for just UIDs with rules:");
+ fout.increaseIndent();
+ size = mUidRules.size();
+ for (int i = 0; i < size; i++) {
+ final int uid = mUidRules.keyAt(i);
+ fout.print("UID=");
+ fout.print(uid);
+ final int uidRules = mUidRules.get(uid, RULE_NONE);
+ fout.print(" rules=");
+ fout.print(uidRulesToString(uidRules));
fout.println();
}
fout.decreaseIndent();
}
-
- size = mPowerSaveWhitelistAppIds.size();
- if (size > 0) {
- fout.println("Power save whitelist app ids:");
- fout.increaseIndent();
- for (int i = 0; i < size; i++) {
- fout.print("UID=");
- fout.print(mPowerSaveWhitelistAppIds.keyAt(i));
- fout.print(": ");
- fout.print(mPowerSaveWhitelistAppIds.valueAt(i));
- fout.println();
- }
- fout.decreaseIndent();
- }
-
- size = mRestrictBackgroundWhitelistUids.size();
- if (size > 0) {
- fout.println("Restrict background whitelist uids:");
- fout.increaseIndent();
- for (int i = 0; i < size; i++) {
- fout.print("UID=");
- fout.print(mRestrictBackgroundWhitelistUids.keyAt(i));
- fout.println();
- }
- fout.decreaseIndent();
- }
-
- size = mDefaultRestrictBackgroundWhitelistUids.size();
- if (size > 0) {
- fout.println("Default restrict background whitelist uids:");
- fout.increaseIndent();
- for (int i = 0; i < size; i++) {
- fout.print("UID=");
- fout.print(mDefaultRestrictBackgroundWhitelistUids.keyAt(i));
- fout.println();
- }
- fout.decreaseIndent();
- }
-
- size = mRestrictBackgroundWhitelistRevokedUids.size();
- if (size > 0) {
- fout.println("Default restrict background whitelist uids revoked by users:");
- fout.increaseIndent();
- for (int i = 0; i < size; i++) {
- fout.print("UID=");
- fout.print(mRestrictBackgroundWhitelistRevokedUids.keyAt(i));
- fout.println();
- }
- fout.decreaseIndent();
- }
-
- final SparseBooleanArray knownUids = new SparseBooleanArray();
- collectKeys(mUidState, knownUids);
- collectKeys(mUidRules, knownUids);
-
- fout.println("Status for all known UIDs:");
- fout.increaseIndent();
- size = knownUids.size();
- for (int i = 0; i < size; i++) {
- final int uid = knownUids.keyAt(i);
- fout.print("UID=");
- fout.print(uid);
-
- final int state = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
- fout.print(" state=");
- fout.print(state);
- if (state <= ActivityManager.PROCESS_STATE_TOP) {
- fout.print(" (fg)");
- } else {
- fout.print(state <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
- ? " (fg svc)" : " (bg)");
- }
-
- final int uidRules = mUidRules.get(uid, RULE_NONE);
- fout.print(" rules=");
- fout.print(uidRulesToString(uidRules));
- fout.println();
- }
- fout.decreaseIndent();
-
- fout.println("Status for just UIDs with rules:");
- fout.increaseIndent();
- size = mUidRules.size();
- for (int i = 0; i < size; i++) {
- final int uid = mUidRules.keyAt(i);
- fout.print("UID=");
- fout.print(uid);
- final int uidRules = mUidRules.get(uid, RULE_NONE);
- fout.print(" rules=");
- fout.print(uidRulesToString(uidRules));
- fout.println();
- }
- fout.decreaseIndent();
}
}
@@ -2419,74 +2472,74 @@
public boolean isUidForeground(int uid) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- synchronized (mRulesLock) {
- return isUidForegroundLocked(uid);
+ synchronized (mUidRulesFirstLock) {
+ return isUidForegroundUL(uid);
}
}
- private boolean isUidForegroundLocked(int uid) {
- return isUidStateForegroundLocked(
+ private boolean isUidForegroundUL(int uid) {
+ return isUidStateForegroundUL(
mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY));
}
- private boolean isUidForegroundOnRestrictBackgroundLocked(int uid) {
+ private boolean isUidForegroundOnRestrictBackgroundUL(int uid) {
final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
- return isProcStateAllowedWhileOnRestrictBackgroundLocked(procState);
+ return isProcStateAllowedWhileOnRestrictBackground(procState);
}
- private boolean isUidForegroundOnRestrictPowerLocked(int uid) {
+ private boolean isUidForegroundOnRestrictPowerUL(int uid) {
final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
return isProcStateAllowedWhileIdleOrPowerSaveMode(procState);
}
- private boolean isUidStateForegroundLocked(int state) {
+ private boolean isUidStateForegroundUL(int state) {
// only really in foreground when screen is also on
- return mScreenOn && state <= ActivityManager.PROCESS_STATE_TOP;
+ return state <= ActivityManager.PROCESS_STATE_TOP;
}
/**
* Process state of UID changed; if needed, will trigger
- * {@link #updateRulesForDataUsageRestrictionsLocked(int)} and
- * {@link #updateRulesForPowerRestrictionsLocked(int)}
+ * {@link #updateRulesForDataUsageRestrictionsUL(int)} and
+ * {@link #updateRulesForPowerRestrictionsUL(int)}
*/
- private void updateUidStateLocked(int uid, int uidState) {
+ private void updateUidStateUL(int uid, int uidState) {
final int oldUidState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
if (oldUidState != uidState) {
// state changed, push updated rules
mUidState.put(uid, uidState);
- updateRestrictBackgroundRulesOnUidStatusChangedLocked(uid, oldUidState, uidState);
+ updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, uidState);
if (isProcStateAllowedWhileIdleOrPowerSaveMode(oldUidState)
!= isProcStateAllowedWhileIdleOrPowerSaveMode(uidState) ) {
if (isUidIdle(uid)) {
- updateRuleForAppIdleLocked(uid);
+ updateRuleForAppIdleUL(uid);
}
if (mDeviceIdleMode) {
- updateRuleForDeviceIdleLocked(uid);
+ updateRuleForDeviceIdleUL(uid);
}
if (mRestrictPower) {
- updateRuleForRestrictPowerLocked(uid);
+ updateRuleForRestrictPowerUL(uid);
}
- updateRulesForPowerRestrictionsLocked(uid);
+ updateRulesForPowerRestrictionsUL(uid);
}
- updateNetworkStats(uid, isUidStateForegroundLocked(uidState));
+ updateNetworkStats(uid, isUidStateForegroundUL(uidState));
}
}
- private void removeUidStateLocked(int uid) {
+ private void removeUidStateUL(int uid) {
final int index = mUidState.indexOfKey(uid);
if (index >= 0) {
final int oldUidState = mUidState.valueAt(index);
mUidState.removeAt(index);
if (oldUidState != ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
- updateRestrictBackgroundRulesOnUidStatusChangedLocked(uid, oldUidState,
+ updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState,
ActivityManager.PROCESS_STATE_CACHED_EMPTY);
if (mDeviceIdleMode) {
- updateRuleForDeviceIdleLocked(uid);
+ updateRuleForDeviceIdleUL(uid);
}
if (mRestrictPower) {
- updateRuleForRestrictPowerLocked(uid);
+ updateRuleForRestrictPowerUL(uid);
}
- updateRulesForPowerRestrictionsLocked(uid);
+ updateRulesForPowerRestrictionsUL(uid);
updateNetworkStats(uid, false);
}
}
@@ -2501,39 +2554,14 @@
}
}
- private void updateRestrictBackgroundRulesOnUidStatusChangedLocked(int uid, int oldUidState,
+ private void updateRestrictBackgroundRulesOnUidStatusChangedUL(int uid, int oldUidState,
int newUidState) {
final boolean oldForeground =
- isProcStateAllowedWhileOnRestrictBackgroundLocked(oldUidState);
+ isProcStateAllowedWhileOnRestrictBackground(oldUidState);
final boolean newForeground =
- isProcStateAllowedWhileOnRestrictBackgroundLocked(newUidState);
+ isProcStateAllowedWhileOnRestrictBackground(newUidState);
if (oldForeground != newForeground) {
- updateRulesForDataUsageRestrictionsLocked(uid);
- }
- }
-
- private void updateScreenOn() {
- synchronized (mRulesLock) {
- try {
- mScreenOn = mPowerManager.isInteractive();
- } catch (RemoteException e) {
- // ignored; service lives in system_server
- }
- updateRulesForScreenLocked();
- }
- }
-
- /**
- * Update rules that might be changed by {@link #mScreenOn} value.
- */
- private void updateRulesForScreenLocked() {
- // only update rules for anyone with foreground activities
- final int size = mUidState.size();
- for (int i = 0; i < size; i++) {
- if (mUidState.valueAt(i) <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
- final int uid = mUidState.keyAt(i);
- updateRestrictionRulesForUidLocked(uid);
- }
+ updateRulesForDataUsageRestrictionsUL(uid);
}
}
@@ -2541,31 +2569,31 @@
return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
}
- static boolean isProcStateAllowedWhileOnRestrictBackgroundLocked(int procState) {
+ static boolean isProcStateAllowedWhileOnRestrictBackground(int procState) {
return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
}
- void updateRulesForRestrictPowerLocked() {
- updateRulesForWhitelistedPowerSaveLocked(mRestrictPower, FIREWALL_CHAIN_POWERSAVE,
+ void updateRulesForPowerSaveUL() {
+ updateRulesForWhitelistedPowerSaveUL(mRestrictPower, FIREWALL_CHAIN_POWERSAVE,
mUidFirewallPowerSaveRules);
}
- void updateRuleForRestrictPowerLocked(int uid) {
- updateRulesForWhitelistedPowerSaveLocked(uid, mRestrictPower, FIREWALL_CHAIN_POWERSAVE);
+ void updateRuleForRestrictPowerUL(int uid) {
+ updateRulesForWhitelistedPowerSaveUL(uid, mRestrictPower, FIREWALL_CHAIN_POWERSAVE);
}
- void updateRulesForDeviceIdleLocked() {
- updateRulesForWhitelistedPowerSaveLocked(mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE,
+ void updateRulesForDeviceIdleUL() {
+ updateRulesForWhitelistedPowerSaveUL(mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE,
mUidFirewallDozableRules);
}
- void updateRuleForDeviceIdleLocked(int uid) {
- updateRulesForWhitelistedPowerSaveLocked(uid, mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE);
+ void updateRuleForDeviceIdleUL(int uid) {
+ updateRulesForWhitelistedPowerSaveUL(uid, mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE);
}
// NOTE: since both fw_dozable and fw_powersave uses the same map
// (mPowerSaveTempWhitelistAppIds) for whitelisting, we can reuse their logic in this method.
- private void updateRulesForWhitelistedPowerSaveLocked(boolean enabled, int chain,
+ private void updateRulesForWhitelistedPowerSaveUL(boolean enabled, int chain,
SparseIntArray rules) {
if (enabled) {
// Sync the whitelists before enabling the chain. We don't care about the rules if
@@ -2596,23 +2624,19 @@
setUidFirewallRules(chain, uidRules);
}
- enableFirewallChainLocked(chain, enabled);
+ enableFirewallChainUL(chain, enabled);
}
- private void updateRulesForNonMeteredNetworksLocked() {
-
- }
-
- private boolean isWhitelistedBatterySaverLocked(int uid) {
+ private boolean isWhitelistedBatterySaverUL(int uid) {
final int appId = UserHandle.getAppId(uid);
return mPowerSaveTempWhitelistAppIds.get(appId) || mPowerSaveWhitelistAppIds.get(appId);
}
// NOTE: since both fw_dozable and fw_powersave uses the same map
// (mPowerSaveTempWhitelistAppIds) for whitelisting, we can reuse their logic in this method.
- private void updateRulesForWhitelistedPowerSaveLocked(int uid, boolean enabled, int chain) {
+ private void updateRulesForWhitelistedPowerSaveUL(int uid, boolean enabled, int chain) {
if (enabled) {
- if (isWhitelistedBatterySaverLocked(uid)
+ if (isWhitelistedBatterySaverUL(uid)
|| isProcStateAllowedWhileIdleOrPowerSaveMode(mUidState.get(uid))) {
setUidFirewallRule(chain, uid, FIREWALL_RULE_ALLOW);
} else {
@@ -2621,7 +2645,7 @@
}
}
- void updateRulesForAppIdleLocked() {
+ void updateRulesForAppIdleUL() {
final SparseIntArray uidRules = mUidFirewallStandbyRules;
uidRules.clear();
@@ -2645,50 +2669,69 @@
setUidFirewallRules(FIREWALL_CHAIN_STANDBY, uidRules);
}
- void updateRuleForAppIdleLocked(int uid) {
+ void updateRuleForAppIdleUL(int uid) {
if (!isUidValidForBlacklistRules(uid)) return;
int appId = UserHandle.getAppId(uid);
if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid)
- && !isUidForegroundOnRestrictPowerLocked(uid)) {
+ && !isUidForegroundOnRestrictPowerUL(uid)) {
setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY);
} else {
setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT);
}
}
- void updateRulesForAppIdleParoleLocked() {
+ void updateRulesForAppIdleParoleUL() {
boolean enableChain = !mUsageStats.isAppIdleParoleOn();
- enableFirewallChainLocked(FIREWALL_CHAIN_STANDBY, enableChain);
+ enableFirewallChainUL(FIREWALL_CHAIN_STANDBY, enableChain);
}
/**
* Update rules that might be changed by {@link #mRestrictBackground},
* {@link #mRestrictPower}, or {@link #mDeviceIdleMode} value.
*/
- private void updateRulesForGlobalChangeLocked(boolean restrictedNetworksChanged) {
+ private void updateRulesForGlobalChangeAL(boolean restrictedNetworksChanged) {
long start;
if (LOGD) start = System.currentTimeMillis();
- updateRulesForDeviceIdleLocked();
- updateRulesForAppIdleLocked();
- updateRulesForRestrictPowerLocked();
- updateRulesForRestrictBackgroundLocked();
- setRestrictBackgroundLocked(mRestrictBackground);
+ updateRulesForRestrictPowerUL();
+ updateRulesForRestrictBackgroundUL();
// If the set of restricted networks may have changed, re-evaluate those.
if (restrictedNetworksChanged) {
- normalizePoliciesLocked();
- updateNetworkRulesLocked();
+ normalizePoliciesNL();
+ updateNetworkRulesNL();
}
if (LOGD) {
final long delta = System.currentTimeMillis() - start;
- Slog.d(TAG, "updateRulesForGlobalChangeLocked(" + restrictedNetworksChanged + ") took "
+ Slog.d(TAG, "updateRulesForGlobalChangeAL(" + restrictedNetworksChanged + ") took "
+ delta + "ms");
}
}
- private void updateRulesForRestrictBackgroundLocked() {
+ private void updateRulesForRestrictPowerUL() {
+ updateRulesForDeviceIdleUL();
+ updateRulesForAppIdleUL();
+ updateRulesForPowerSaveUL();
+ updateRulesForAllAppsUL(TYPE_RESTRICT_POWER);
+ }
+
+ private void updateRulesForRestrictBackgroundUL() {
+ updateRulesForAllAppsUL(TYPE_RESTRICT_BACKGROUND);
+ }
+
+ private static final int TYPE_RESTRICT_BACKGROUND = 1;
+ private static final int TYPE_RESTRICT_POWER = 2;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false, value = {
+ TYPE_RESTRICT_BACKGROUND,
+ TYPE_RESTRICT_POWER,
+ })
+ public @interface RestrictType {
+ }
+
+ // TODO: refactor / consolidate all those updateXyz methods, there are way too many of them...
+ private void updateRulesForAllAppsUL(@RestrictType int type) {
final PackageManager pm = mContext.getPackageManager();
// update rules for all installed applications
@@ -2705,13 +2748,21 @@
for (int j = 0; j < appsSize; j++) {
final ApplicationInfo app = apps.get(j);
final int uid = UserHandle.getUid(user.id, app.uid);
- updateRulesForDataUsageRestrictionsLocked(uid);
- updateRulesForPowerRestrictionsLocked(uid);
+ switch (type) {
+ case TYPE_RESTRICT_BACKGROUND:
+ updateRulesForDataUsageRestrictionsUL(uid);
+ break;
+ case TYPE_RESTRICT_POWER:
+ updateRulesForPowerRestrictionsUL(uid);
+ break;
+ default:
+ Slog.w(TAG, "Invalid type for updateRulesForAllApps: " + type);
+ }
}
}
}
- private void updateRulesForTempWhitelistChangeLocked() {
+ private void updateRulesForTempWhitelistChangeUL() {
final List<UserInfo> users = mUserManager.getUsers();
for (int i = 0; i < users.size(); i++) {
final UserInfo user = users.get(i);
@@ -2719,11 +2770,11 @@
int appId = mPowerSaveTempWhitelistAppIds.keyAt(j);
int uid = UserHandle.getUid(user.id, appId);
// Update external firewall rules.
- updateRuleForAppIdleLocked(uid);
- updateRuleForDeviceIdleLocked(uid);
- updateRuleForRestrictPowerLocked(uid);
+ updateRuleForAppIdleUL(uid);
+ updateRuleForDeviceIdleUL(uid);
+ updateRuleForRestrictPowerUL(uid);
// Update internal rules.
- updateRulesForPowerRestrictionsLocked(uid);
+ updateRulesForPowerRestrictionsUL(uid);
}
}
}
@@ -2787,17 +2838,17 @@
*
* <p>This method changes both the external firewall rules and the internal state.
*/
- private void updateRestrictionRulesForUidLocked(int uid) {
+ private void updateRestrictionRulesForUidUL(int uid) {
// Methods below only changes the firewall rules for the power-related modes.
- updateRuleForDeviceIdleLocked(uid);
- updateRuleForAppIdleLocked(uid);
- updateRuleForRestrictPowerLocked(uid);
+ updateRuleForDeviceIdleUL(uid);
+ updateRuleForAppIdleUL(uid);
+ updateRuleForRestrictPowerUL(uid);
// Update internal state for power-related modes.
- updateRulesForPowerRestrictionsLocked(uid);
+ updateRulesForPowerRestrictionsUL(uid);
// Update firewall and internal rules for Data Saver Mode.
- updateRulesForDataUsageRestrictionsLocked(uid);
+ updateRulesForDataUsageRestrictionsUL(uid);
}
/**
@@ -2819,7 +2870,7 @@
* {@link #setUidPolicy(int, int)} and {@link #addRestrictBackgroundWhitelistedUid(int)} /
* {@link #removeRestrictBackgroundWhitelistedUid(int)} methods (for blacklist and whitelist
* respectively): these methods set the proper internal state (blacklist / whitelist), then call
- * this ({@link #updateRulesForDataUsageRestrictionsLocked(int)}) to propagate the rules to
+ * this ({@link #updateRulesForDataUsageRestrictionsUL(int)}) to propagate the rules to
* {@link INetworkManagementService}, but this method should also be called in events (like
* Data Saver Mode flips or UID state changes) that might affect the foreground app, since the
* following rules should also be applied:
@@ -2839,15 +2890,15 @@
* <p>The {@link #mUidRules} map is used to define the transtion of states of an UID.
*
*/
- private void updateRulesForDataUsageRestrictionsLocked(int uid) {
- updateRulesForDataUsageRestrictionsLocked(uid, false);
+ private void updateRulesForDataUsageRestrictionsUL(int uid) {
+ updateRulesForDataUsageRestrictionsUL(uid, false);
}
/**
- * Overloaded version of {@link #updateRulesForDataUsageRestrictionsLocked(int)} called when an
+ * Overloaded version of {@link #updateRulesForDataUsageRestrictionsUL(int)} called when an
* app is removed - it ignores the UID validity check.
*/
- private void updateRulesForDataUsageRestrictionsLocked(int uid, boolean uidDeleted) {
+ private void updateRulesForDataUsageRestrictionsUL(int uid, boolean uidDeleted) {
if (!uidDeleted && !isUidValidForWhitelistRules(uid)) {
if (LOGD) Slog.d(TAG, "no need to update restrict data rules for uid " + uid);
return;
@@ -2855,7 +2906,7 @@
final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
final int oldUidRules = mUidRules.get(uid, RULE_NONE);
- final boolean isForeground = isUidForegroundOnRestrictBackgroundLocked(uid);
+ final boolean isForeground = isUidForegroundOnRestrictBackgroundUL(uid);
final boolean isBlacklisted = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
final boolean isWhitelisted = mRestrictBackgroundWhitelistUids.get(uid);
@@ -2879,7 +2930,7 @@
final int newUidRules = newRule | (oldUidRules & MASK_ALL_NETWORKS);
if (LOGV) {
- Log.v(TAG, "updateRuleForRestrictBackgroundLocked(" + uid + ")"
+ Log.v(TAG, "updateRuleForRestrictBackgroundUL(" + uid + ")"
+ ": isForeground=" +isForeground
+ ", isBlacklisted=" + isBlacklisted
+ ", isWhitelisted=" + isWhitelisted
@@ -2895,12 +2946,8 @@
mUidRules.put(uid, newUidRules);
}
- boolean changed = false;
-
// Second step: apply bw changes based on change of state.
if (newRule != oldRule) {
- changed = true;
-
if ((newRule & RULE_TEMPORARY_ALLOW_METERED) != 0) {
// Temporarily whitelist foreground app, removing from blacklist if necessary
// (since bw_penalty_box prevails over bw_happy_box).
@@ -2972,7 +3019,7 @@
* <p>
* <strong>NOTE: </strong>This method does not update the firewall rules on {@code netd}.
*/
- private void updateRulesForPowerRestrictionsLocked(int uid) {
+ private void updateRulesForPowerRestrictionsUL(int uid) {
if (!isUidValidForBlacklistRules(uid)) {
if (LOGD) Slog.d(TAG, "no need to update restrict power rules for uid " + uid);
return;
@@ -2980,11 +3027,10 @@
final boolean isIdle = isUidIdle(uid);
final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode;
- final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
final int oldUidRules = mUidRules.get(uid, RULE_NONE);
- final boolean isForeground = isUidForegroundOnRestrictPowerLocked(uid);
+ final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid);
- final boolean isWhitelisted = isWhitelistedBatterySaverLocked(uid);
+ final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid);
final int oldRule = oldUidRules & MASK_ALL_NETWORKS;
int newRule = RULE_NONE;
@@ -3003,7 +3049,7 @@
final int newUidRules = (oldUidRules & MASK_METERED_NETWORKS) | newRule;
if (LOGV) {
- Log.v(TAG, "updateRulesForNonMeteredNetworksLocked(" + uid + ")"
+ Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")"
+ ", isIdle: " + isIdle
+ ", mRestrictPower: " + mRestrictPower
+ ", mDeviceIdleMode: " + mDeviceIdleMode
@@ -3048,9 +3094,9 @@
final int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
if (LOGV) Log.v(TAG, "onAppIdleStateChanged(): uid=" + uid + ", idle=" + idle);
- synchronized (mRulesLock) {
- updateRuleForAppIdleLocked(uid);
- updateRulesForPowerRestrictionsLocked(uid);
+ synchronized (mUidRulesFirstLock) {
+ updateRuleForAppIdleUL(uid);
+ updateRulesForPowerRestrictionsUL(uid);
}
} catch (NameNotFoundException nnfe) {
}
@@ -3058,8 +3104,8 @@
@Override
public void onParoleStateChanged(boolean isParoleOn) {
- synchronized (mRulesLock) {
- updateRulesForAppIdleParoleLocked();
+ synchronized (mUidRulesFirstLock) {
+ updateRulesForAppIdleParoleUL();
}
}
}
@@ -3144,7 +3190,7 @@
final String iface = (String) msg.obj;
maybeRefreshTrustedTime();
- synchronized (mRulesLock) {
+ synchronized (mNetworkPoliciesSecondLock) {
if (mMeteredIfaces.contains(iface)) {
try {
// force stats update to make sure we have
@@ -3154,8 +3200,8 @@
// ignored; service lives in system_server
}
- updateNetworkEnabledLocked();
- updateNotificationsLocked();
+ updateNetworkEnabledNL();
+ updateNotificationsNL();
}
}
return true;
@@ -3245,10 +3291,6 @@
}
return true;
}
- case MSG_SCREEN_ON_CHANGED: {
- updateScreenOn();
- return true;
- }
case MSG_UPDATE_INTERFACE_QUOTA: {
removeInterfaceQuota((String) msg.obj);
// int params need to be stitched back into a long
@@ -3355,7 +3397,7 @@
/**
* Add or remove a uid to the firewall blacklist for all network ifaces.
*/
- private void enableFirewallChainLocked(int chain, boolean enable) {
+ private void enableFirewallChainUL(int chain, boolean enable) {
if (mFirewallChainStates.indexOfKey(chain) >= 0 &&
mFirewallChainStates.get(chain) == enable) {
// All is the same, nothing to do.
@@ -3484,9 +3526,9 @@
@Override
public void onPackageRemoved(String packageName, int uid) {
if (LOGV) Slog.v(TAG, "onPackageRemoved: " + packageName + " ->" + uid);
- synchronized (mRulesLock) {
- removeRestrictBackgroundWhitelistedUidLocked(uid, true, true);
- updateRestrictionRulesForUidLocked(uid);
+ synchronized (mUidRulesFirstLock) {
+ removeRestrictBackgroundWhitelistedUidUL(uid, true, true);
+ updateRestrictionRulesForUidUL(uid);
}
}
}
@@ -3495,11 +3537,13 @@
@Override
public void resetUserState(int userId) {
- synchronized (mRulesLock) {
- boolean changed = removeUserStateLocked(userId, false);
- changed = addDefaultRestrictBackgroundWhitelistUidsLocked(userId) || changed;
+ synchronized (mUidRulesFirstLock) {
+ boolean changed = removeUserStateUL(userId, false);
+ changed = addDefaultRestrictBackgroundWhitelistUidsUL(userId) || changed;
if (changed) {
- writePolicyLocked();
+ synchronized (mNetworkPoliciesSecondLock) {
+ writePolicyAL();
+ }
}
}
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 23c111e..4658c046 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -31,6 +31,7 @@
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.SET_FOREGROUND;
+import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileWildcard;
@@ -79,6 +80,7 @@
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
import android.net.NetworkIdentity;
import android.net.NetworkInfo;
import android.net.NetworkState;
@@ -179,6 +181,11 @@
private static final String PREFIX_UID_TAG = "uid_tag";
/**
+ * Virtual network interface for video telephony. This is for VT data usage counting purpose.
+ */
+ public static final String VT_INTERFACE = "vt_data0";
+
+ /**
* Settings that can be changed externally.
*/
public interface NetworkStatsSettings {
@@ -967,6 +974,23 @@
if (baseIface != null) {
findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident);
findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident);
+
+ // Build a separate virtual interface for VT (Video Telephony) data usage.
+ // Only do this when IMS is not metered, but VT is metered.
+ // If IMS is metered, then the IMS network usage has already included VT usage.
+ // VT is considered always metered in framework's layer. If VT is not metered
+ // per carrier's policy, modem will report 0 usage for VT calls.
+ if (state.networkCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.getMetered()) {
+
+ // Copy the identify from IMS one but mark it as metered.
+ NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(),
+ ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(),
+ ident.getRoaming(), true);
+ findOrCreateNetworkIdentitySet(mActiveIfaces, VT_INTERFACE).add(vtIdent);
+ findOrCreateNetworkIdentitySet(mActiveUidIfaces, VT_INTERFACE).add(vtIdent);
+ }
+
if (isMobile) {
mobileIfaces.add(baseIface);
}
@@ -1004,9 +1028,9 @@
private void recordSnapshotLocked(long currentTime) throws RemoteException {
// snapshot and record current counters; read UID stats first to
- // avoid overcounting dev stats.
+ // avoid over counting dev stats.
final NetworkStats uidSnapshot = getNetworkStatsUidDetail();
- final NetworkStats xtSnapshot = mNetworkManager.getNetworkStatsSummaryXt();
+ final NetworkStats xtSnapshot = getNetworkStatsXtAndVt();
final NetworkStats devSnapshot = mNetworkManager.getNetworkStatsSummaryDev();
@@ -1312,6 +1336,42 @@
}
/**
+ * Return snapshot of current XT plus VT statistics.
+ */
+ private NetworkStats getNetworkStatsXtAndVt() throws RemoteException {
+ final NetworkStats xtSnapshot = mNetworkManager.getNetworkStatsSummaryXt();
+
+ TelephonyManager tm = (TelephonyManager) mContext.getSystemService(
+ Context.TELEPHONY_SERVICE);
+
+ long usage = tm.getVtDataUsage();
+
+ if (LOGV) Slog.d(TAG, "VT call data usage = " + usage);
+
+ final NetworkStats vtSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 1);
+
+ final NetworkStats.Entry entry = new NetworkStats.Entry();
+ entry.iface = VT_INTERFACE;
+ entry.uid = -1;
+ entry.set = TAG_ALL;
+ entry.tag = TAG_NONE;
+
+ // Since modem only tell us the total usage instead of each usage for RX and TX,
+ // we need to split it up (though it might not quite accurate). At
+ // least we can make sure the data usage report to the user will still be accurate.
+ entry.rxBytes = usage / 2;
+ entry.rxPackets = 0;
+ entry.txBytes = usage - entry.rxBytes;
+ entry.txPackets = 0;
+ vtSnapshot.combineValues(entry);
+
+ // Merge VT int XT
+ xtSnapshot.combineAllValues(vtSnapshot);
+
+ return xtSnapshot;
+ }
+
+ /**
* Return snapshot of current tethering statistics. Will return empty
* {@link NetworkStats} if any problems are encountered.
*/
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 62fe70c..2fab288 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -17,6 +17,8 @@
package com.android.server.notification;
import android.annotation.NonNull;
+import android.app.INotificationManager;
+import android.app.NotificationManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -161,6 +163,25 @@
}
}
+ @Override
+ public void onPackagesChanged(boolean removingPackage, String[] pkgList) {
+ if (removingPackage) {
+ INotificationManager inm = NotificationManager.getService();
+
+ if (pkgList != null && (pkgList.length > 0)) {
+ for (String pkgName : pkgList) {
+ try {
+ inm.removeAutomaticZenRules(pkgName);
+ inm.setNotificationPolicyAccessGranted(pkgName, false);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to clean up rules for " + pkgName, e);
+ }
+ }
+ }
+ }
+ super.onPackagesChanged(removingPackage, pkgList);
+ }
+
public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
synchronized(mMutex) {
return checkServiceTokenLocked(provider);
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index dc85dd7..14e2ba3 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -217,8 +217,8 @@
return mEnabledServicesPackageNames.contains(pkg);
}
- public void onPackagesChanged(boolean queryReplace, String[] pkgList) {
- if (DEBUG) Slog.d(TAG, "onPackagesChanged queryReplace=" + queryReplace
+ public void onPackagesChanged(boolean removingPackage, String[] pkgList) {
+ if (DEBUG) Slog.d(TAG, "onPackagesChanged removingPackage=" + removingPackage
+ " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList))
+ " mEnabledServicesPackageNames=" + mEnabledServicesPackageNames);
boolean anyServicesInvolved = false;
@@ -234,7 +234,7 @@
if (anyServicesInvolved) {
// if we're not replacing a package, clean up orphaned bits
- if (!queryReplace) {
+ if (removingPackage) {
updateSettingsAccordingToInstalledServices();
rebuildRestoredPackages();
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ee09e16..bb55240 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -180,7 +180,7 @@
= SystemProperties.getBoolean("debug.child_notifs", true);
static final int MAX_PACKAGE_NOTIFICATIONS = 50;
- static final float DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE = 50f;
+ static final float DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE = 10f;
// message codes
static final int MESSAGE_TIMEOUT = 2;
@@ -305,6 +305,7 @@
private RankingHandler mRankingHandler;
private long mLastOverRateLogTime;
private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
+ private String mSystemNotificationSound;
private static class Archive {
final int mBufferSize;
@@ -695,9 +696,9 @@
int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
UserHandle.USER_ALL);
String pkgList[] = null;
- boolean queryReplace = queryRemove &&
- intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
- if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
+ boolean removingPackage = queryRemove &&
+ !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+ if (DBG) Slog.i(TAG, "action=" + action + " removing=" + removingPackage);
if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
} else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
@@ -746,10 +747,10 @@
}
}
}
- mListeners.onPackagesChanged(queryReplace, pkgList);
- mRankerServices.onPackagesChanged(queryReplace, pkgList);
- mConditionProviders.onPackagesChanged(queryReplace, pkgList);
- mRankingHelper.onPackagesChanged(queryReplace, pkgList);
+ mListeners.onPackagesChanged(removingPackage, pkgList);
+ mRankerServices.onPackagesChanged(removingPackage, pkgList);
+ mConditionProviders.onPackagesChanged(removingPackage, pkgList);
+ mRankingHelper.onPackagesChanged(removingPackage, pkgList);
}
}
};
@@ -817,6 +818,8 @@
private final class SettingsObserver extends ContentObserver {
private final Uri NOTIFICATION_LIGHT_PULSE_URI
= Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
+ private final Uri NOTIFICATION_SOUND_URI
+ = Settings.System.getUriFor(Settings.System.NOTIFICATION_SOUND);
private final Uri NOTIFICATION_RATE_LIMIT_URI
= Settings.Global.getUriFor(Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE);
@@ -828,6 +831,8 @@
ContentResolver resolver = getContext().getContentResolver();
resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(NOTIFICATION_SOUND_URI,
+ false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI,
false, this, UserHandle.USER_ALL);
update(null);
@@ -851,6 +856,10 @@
mMaxPackageEnqueueRate = Settings.Global.getFloat(resolver,
Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, mMaxPackageEnqueueRate);
}
+ if (uri == null || NOTIFICATION_SOUND_URI.equals(uri)) {
+ mSystemNotificationSound = Settings.System.getString(resolver,
+ Settings.System.NOTIFICATION_SOUND);
+ }
}
}
@@ -903,6 +912,11 @@
mHandler = handler;
}
+ @VisibleForTesting
+ void setSystemNotificationSound(String systemNotificationSound) {
+ mSystemNotificationSound = systemNotificationSound;
+ }
+
@Override
public void onStart() {
Resources resources = getContext().getResources();
@@ -1837,7 +1851,7 @@
enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode");
final long identity = Binder.clearCallingIdentity();
try {
- mZenModeHelper.setManualZenMode(mode, conditionId, reason);
+ mZenModeHelper.setManualZenMode(mode, conditionId, null, reason);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -1914,7 +1928,7 @@
if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
final long identity = Binder.clearCallingIdentity();
try {
- mZenModeHelper.setManualZenMode(zen, null, "setInterruptionFilter");
+ mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter");
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2272,6 +2286,7 @@
.setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
.setFlag(Notification.FLAG_GROUP_SUMMARY, true)
.setColor(adjustedSbn.getNotification().color)
+ .setLocalOnly(true)
.build();
summaryNotification.extras.putAll(extras);
Intent appIntent = getContext().getPackageManager()
@@ -2523,20 +2538,32 @@
mUsageStats.registerEnqueuedByApp(pkg);
+
+ if (pkg == null || notification == null) {
+ throw new IllegalArgumentException("null not allowed: pkg=" + pkg
+ + " id=" + id + " notification=" + notification);
+ }
+ final StatusBarNotification n = new StatusBarNotification(
+ pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,
+ user);
+
// Limit the number of notifications that any given package except the android
// package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
if (!isSystemNotification && !isNotificationFromListener) {
synchronized (mNotificationList) {
- final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
- if (appEnqueueRate > mMaxPackageEnqueueRate) {
- mUsageStats.registerOverRateQuota(pkg);
- final long now = SystemClock.elapsedRealtime();
- if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
- Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
- + ". Shedding events. package=" + pkg);
- mLastOverRateLogTime = now;
+ if(mNotificationsByKey.get(n.getKey()) != null) {
+ // this is an update, rate limit updates only
+ final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
+ if (appEnqueueRate > mMaxPackageEnqueueRate) {
+ mUsageStats.registerOverRateQuota(pkg);
+ final long now = SystemClock.elapsedRealtime();
+ if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
+ Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
+ + ". Shedding events. package=" + pkg);
+ mLastOverRateLogTime = now;
+ }
+ return;
}
- return;
}
int count = 0;
@@ -2559,11 +2586,6 @@
}
}
- if (pkg == null || notification == null) {
- throw new IllegalArgumentException("null not allowed: pkg=" + pkg
- + " id=" + id + " notification=" + notification);
- }
-
// Whitelist pending intents.
if (notification.allPendingIntents != null) {
final int intentCount = notification.allPendingIntents.size();
@@ -2586,9 +2608,6 @@
Notification.PRIORITY_MAX);
// setup local book-keeping
- final StatusBarNotification n = new StatusBarNotification(
- pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,
- user);
final NotificationRecord r = new NotificationRecord(getContext(), n);
mHandler.post(new EnqueueNotificationRunnable(userId, r));
@@ -2824,9 +2843,7 @@
soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
// check to see if the default notification sound is silent
- ContentResolver resolver = getContext().getContentResolver();
- hasValidSound = Settings.System.getString(resolver,
- Settings.System.NOTIFICATION_SOUND) != null;
+ hasValidSound = mSystemNotificationSound != null;
} else if (notification.sound != null) {
soundUri = notification.sound;
hasValidSound = (soundUri != null);
@@ -3127,6 +3144,12 @@
}
}
+ private void recordCallerLocked(NotificationRecord record) {
+ if (mZenModeHelper.isCall(record)) {
+ mZenModeHelper.recordCaller(record);
+ }
+ }
+
// let zen mode evaluate this record
private void applyZenModeLocked(NotificationRecord record) {
record.setIntercepted(mZenModeHelper.shouldIntercept(record));
@@ -3266,6 +3289,10 @@
}
private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
+
+ // Record caller.
+ recordCallerLocked(r);
+
// tell the app
if (sendDelete) {
if (r.getNotification().deleteIntent != null) {
@@ -3867,14 +3894,14 @@
}
@Override
- public void onPackagesChanged(boolean queryReplace, String[] pkgList) {
- if (DEBUG) Slog.d(TAG, "onPackagesChanged queryReplace=" + queryReplace
+ public void onPackagesChanged(boolean removingPackage, String[] pkgList) {
+ if (DEBUG) Slog.d(TAG, "onPackagesChanged removingPackage=" + removingPackage
+ " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList)));
if (mRankerServicePackageName == null) {
return;
}
- if (pkgList != null && (pkgList.length > 0)) {
+ if (pkgList != null && (pkgList.length > 0) && !removingPackage) {
for (String pkgName : pkgList) {
if (mRankerServicePackageName.equals(pkgName)) {
registerRanker();
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 78b3f41..9048402 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -504,8 +504,8 @@
return packageBans;
}
- public void onPackagesChanged(boolean queryReplace, String[] pkgList) {
- if (queryReplace || pkgList == null || pkgList.length == 0
+ public void onPackagesChanged(boolean removingPackage, String[] pkgList) {
+ if (!removingPackage || pkgList == null || pkgList.length == 0
|| mRestoredWithoutUids.isEmpty()) {
return; // nothing to do
}
diff --git a/services/core/java/com/android/server/notification/ScheduleCalendar.java b/services/core/java/com/android/server/notification/ScheduleCalendar.java
index 22ca702..9e8b2e3 100644
--- a/services/core/java/com/android/server/notification/ScheduleCalendar.java
+++ b/services/core/java/com/android/server/notification/ScheduleCalendar.java
@@ -82,15 +82,13 @@
if (end <= start) {
end = addDays(end, 1);
}
- boolean isInSchedule =
- isInSchedule(-1, time, start, end) || isInSchedule(0, time, start, end);
- if (isInSchedule && mSchedule.exitAtAlarm
+ return isInSchedule(-1, time, start, end) || isInSchedule(0, time, start, end);
+ }
+
+ public boolean shouldExitForAlarm(long time) {
+ return mSchedule.exitAtAlarm
&& mSchedule.nextAlarm != 0
- && time >= mSchedule.nextAlarm) {
- return false;
- } else {
- return isInSchedule;
- }
+ && time >= mSchedule.nextAlarm;
}
private boolean isInSchedule(int daysOffset, long time, long start, long end) {
diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
index 8197544..32d03ce 100644
--- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
+++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
@@ -25,11 +25,15 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
+import android.os.Binder;
+import android.provider.Settings;
import android.service.notification.Condition;
import android.service.notification.IConditionProvider;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ScheduleInfo;
+import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
@@ -53,9 +57,13 @@
private static final String ACTION_EVALUATE = SIMPLE_NAME + ".EVALUATE";
private static final int REQUEST_CODE_EVALUATE = 1;
private static final String EXTRA_TIME = "time";
+ private static final String SEPARATOR = ";";
+ private static final String SCP_SETTING = "snoozed_schedule_condition_provider";
+
private final Context mContext = this;
private final ArrayMap<Uri, ScheduleCalendar> mSubscriptions = new ArrayMap<>();
+ private ArraySet<Uri> mSnoozed = new ArraySet<>();
private AlarmManager mAlarmManager;
private boolean mConnected;
@@ -90,6 +98,7 @@
pw.print(" ");
pw.println(mSubscriptions.get(conditionId).toString());
}
+ pw.println(" snoozed due to alarm: " + TextUtils.join(SEPARATOR, mSnoozed));
dumpUpcomingTime(pw, "mNextAlarmTime", mNextAlarmTime, now);
}
@@ -97,6 +106,7 @@
public void onConnected() {
if (DEBUG) Slog.d(TAG, "onConnected");
mConnected = true;
+ readSnoozed();
}
@Override
@@ -126,6 +136,7 @@
public void onUnsubscribe(Uri conditionId) {
if (DEBUG) Slog.d(TAG, "onUnsubscribe " + conditionId);
mSubscriptions.remove(conditionId);
+ removeSnoozed(conditionId);
evaluateSubscriptions();
}
@@ -150,10 +161,16 @@
for (Uri conditionId : mSubscriptions.keySet()) {
final ScheduleCalendar cal = mSubscriptions.get(conditionId);
if (cal != null && cal.isInSchedule(now)) {
- notifyCondition(conditionId, Condition.STATE_TRUE, "meetsSchedule");
+ if (conditionSnoozed(conditionId) || cal.shouldExitForAlarm(now)) {
+ notifyCondition(conditionId, Condition.STATE_FALSE, "alarmCanceled");
+ addSnoozed(conditionId);
+ } else {
+ notifyCondition(conditionId, Condition.STATE_TRUE, "meetsSchedule");
+ }
cal.maybeSetNextAlarm(now, nextUserAlarmTime);
} else {
notifyCondition(conditionId, Condition.STATE_FALSE, "!meetsSchedule");
+ removeSnoozed(conditionId);
if (nextUserAlarmTime == 0) {
cal.maybeSetNextAlarm(now, nextUserAlarmTime);
}
@@ -194,7 +211,7 @@
return info != null ? info.getTriggerTime() : 0;
}
- private static boolean meetsSchedule(ScheduleCalendar cal, long time) {
+ private boolean meetsSchedule(ScheduleCalendar cal, long time) {
return cal != null && cal.isInSchedule(time);
}
@@ -237,6 +254,62 @@
return new Condition(id, summary, line1, line2, 0, state, Condition.FLAG_RELEVANT_ALWAYS);
}
+ private boolean conditionSnoozed(Uri conditionId) {
+ synchronized (mSnoozed) {
+ return mSnoozed.contains(conditionId);
+ }
+ }
+
+ private void addSnoozed(Uri conditionId) {
+ synchronized (mSnoozed) {
+ mSnoozed.add(conditionId);
+ saveSnoozedLocked();
+ }
+ }
+
+ private void removeSnoozed(Uri conditionId) {
+ synchronized (mSnoozed) {
+ mSnoozed.remove(conditionId);
+ saveSnoozedLocked();
+ }
+ }
+
+ public void saveSnoozedLocked() {
+ final String setting = TextUtils.join(SEPARATOR, mSnoozed);
+ final int currentUser = ActivityManager.getCurrentUser();
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ SCP_SETTING,
+ setting,
+ currentUser);
+ }
+
+ public void readSnoozed() {
+ synchronized (mSnoozed) {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ final String setting = Settings.Secure.getStringForUser(
+ mContext.getContentResolver(),
+ SCP_SETTING,
+ ActivityManager.getCurrentUser());
+ if (setting != null) {
+ final String[] tokens = setting.split(SEPARATOR);
+ for (int i = 0; i < tokens.length; i++) {
+ String token = tokens[i];
+ if (token != null) {
+ token = token.trim();
+ }
+ if (TextUtils.isEmpty(token)) {
+ continue;
+ }
+ mSnoozed.add(Uri.parse(token));
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index 86ca97d..1c12a96 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -147,6 +147,7 @@
if (mConditionProviders.subscribeIfNecessary(rule.component, rule.conditionId)) {
mSubscriptions.put(rule.conditionId, rule.component);
} else {
+ rule.condition = null;
if (DEBUG) Log.d(TAG, "zmc failed to subscribe");
}
}
diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java
index 80dc523..cbaad46 100644
--- a/services/core/java/com/android/server/notification/ZenModeFiltering.java
+++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java
@@ -81,7 +81,9 @@
if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) return false; // nothing gets through
if (zen == Global.ZEN_MODE_ALARMS) return false; // not an alarm
if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
- if (config.allowRepeatCallers && REPEAT_CALLERS.isRepeat(context, extras)) return true;
+ if (config.allowRepeatCallers && REPEAT_CALLERS.isRepeat(context, extras)) {
+ return true;
+ }
if (!config.allowCalls) return false; // no other calls get through
if (validator != null) {
final float contactAffinity = validator.getContactAffinity(userHandle, extras,
@@ -97,6 +99,10 @@
? record.sbn.getNotification().extras : null;
}
+ protected void recordCall(NotificationRecord record) {
+ REPEAT_CALLERS.recordCall(mContext, extras(record));
+ }
+
public boolean shouldIntercept(int zen, ZenModeConfig config, NotificationRecord record) {
if (isSystem(record)) {
return false;
@@ -233,28 +239,45 @@
}
private static class RepeatCallers {
+ // Person : time
private final ArrayMap<String, Long> mCalls = new ArrayMap<>();
private int mThresholdMinutes;
+ private synchronized void recordCall(Context context, Bundle extras) {
+ setThresholdMinutes(context);
+ if (mThresholdMinutes <= 0 || extras == null) return;
+ final String peopleString = peopleString(extras);
+ if (peopleString == null) return;
+ final long now = System.currentTimeMillis();
+ cleanUp(mCalls, now);
+ mCalls.put(peopleString, now);
+ }
+
private synchronized boolean isRepeat(Context context, Bundle extras) {
- if (mThresholdMinutes <= 0) {
- mThresholdMinutes = context.getResources().getInteger(com.android.internal.R.integer
- .config_zen_repeat_callers_threshold);
- }
+ setThresholdMinutes(context);
if (mThresholdMinutes <= 0 || extras == null) return false;
final String peopleString = peopleString(extras);
if (peopleString == null) return false;
final long now = System.currentTimeMillis();
- final int N = mCalls.size();
+ cleanUp(mCalls, now);
+ return mCalls.containsKey(peopleString);
+ }
+
+ private synchronized void cleanUp(ArrayMap<String, Long> calls, long now) {
+ final int N = calls.size();
for (int i = N - 1; i >= 0; i--) {
final long time = mCalls.valueAt(i);
if (time > now || (now - time) > mThresholdMinutes * 1000 * 60) {
- mCalls.removeAt(i);
+ calls.removeAt(i);
}
}
- final boolean isRepeat = mCalls.containsKey(peopleString);
- mCalls.put(peopleString, now);
- return isRepeat;
+ }
+
+ private void setThresholdMinutes(Context context) {
+ if (mThresholdMinutes <= 0) {
+ mThresholdMinutes = context.getResources().getInteger(com.android.internal.R.integer
+ .config_zen_repeat_callers_threshold);
+ }
}
private static String peopleString(Bundle extras) {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 6864ed8..c22bfb3 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -54,7 +54,6 @@
import android.service.notification.ZenModeConfig.EventInfo;
import android.service.notification.ZenModeConfig.ScheduleInfo;
import android.service.notification.ZenModeConfig.ZenRule;
-import android.text.TextUtils;
import android.util.AndroidRuntimeException;
import android.util.Log;
import android.util.SparseArray;
@@ -140,8 +139,7 @@
ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity) {
synchronized (mConfig) {
return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConfig, userHandle,
- extras,
- validator, contactsTimeoutMs, timeoutAffinity);
+ extras, validator, contactsTimeoutMs, timeoutAffinity);
}
}
@@ -149,6 +147,10 @@
return mFiltering.isCall(record);
}
+ public void recordCaller(NotificationRecord record) {
+ mFiltering.recordCall(record);
+ }
+
public boolean shouldIntercept(NotificationRecord record) {
synchronized (mConfig) {
return mFiltering.shouldIntercept(mZenMode, mConfig, record);
@@ -229,7 +231,7 @@
public void requestFromListener(ComponentName name, int filter) {
final int newZen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
if (newZen != -1) {
- setManualZenMode(newZen, null,
+ setManualZenMode(newZen, null, name != null ? name.getPackageName() : null,
"listener:" + (name != null ? name.flattenToShortString() : null));
}
}
@@ -452,11 +454,11 @@
rule.creationTime);
}
- public void setManualZenMode(int zenMode, Uri conditionId, String reason) {
- setManualZenMode(zenMode, conditionId, reason, true /*setRingerMode*/);
+ public void setManualZenMode(int zenMode, Uri conditionId, String caller, String reason) {
+ setManualZenMode(zenMode, conditionId, reason, caller, true /*setRingerMode*/);
}
- private void setManualZenMode(int zenMode, Uri conditionId, String reason,
+ private void setManualZenMode(int zenMode, Uri conditionId, String reason, String caller,
boolean setRingerMode) {
ZenModeConfig newConfig;
synchronized (mConfig) {
@@ -478,6 +480,7 @@
newRule.enabled = true;
newRule.zenMode = zenMode;
newRule.conditionId = conditionId;
+ newRule.enabler = caller;
newConfig.manualRule = newRule;
}
setConfigLocked(newConfig, reason, setRingerMode);
@@ -950,7 +953,8 @@
break;
}
if (newZen != -1) {
- setManualZenMode(newZen, null, "ringerModeInternal", false /*setRingerMode*/);
+ setManualZenMode(newZen, null, "ringerModeInternal", null,
+ false /*setRingerMode*/);
}
if (isChange || newZen != -1 || ringerModeExternal != ringerModeExternalOut) {
@@ -988,7 +992,8 @@
break;
}
if (newZen != -1) {
- setManualZenMode(newZen, null, "ringerModeExternal", false /*setRingerMode*/);
+ setManualZenMode(newZen, null, "ringerModeExternal", caller,
+ false /*setRingerMode*/);
}
ZenLog.traceSetRingerModeExternal(ringerModeOld, ringerModeNew, caller,
diff --git a/services/core/java/com/android/server/os/SchedulingPolicyService.java b/services/core/java/com/android/server/os/SchedulingPolicyService.java
index f98012b..62c9f4c 100644
--- a/services/core/java/com/android/server/os/SchedulingPolicyService.java
+++ b/services/core/java/com/android/server/os/SchedulingPolicyService.java
@@ -55,7 +55,8 @@
Process.setThreadGroup(tid, Binder.getCallingPid() == pid ?
Process.THREAD_GROUP_AUDIO_SYS : Process.THREAD_GROUP_AUDIO_APP);
// must be in this order or it fails the schedulability constraint
- Process.setThreadScheduler(tid, Process.SCHED_FIFO, prio);
+ Process.setThreadScheduler(tid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK,
+ prio);
} catch (RuntimeException e) {
return PackageManager.PERMISSION_DENIED;
}
diff --git a/services/core/java/com/android/server/pm/AbstractStatsBase.java b/services/core/java/com/android/server/pm/AbstractStatsBase.java
new file mode 100644
index 0000000..612c476
--- /dev/null
+++ b/services/core/java/com/android/server/pm/AbstractStatsBase.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.os.Environment;
+import android.os.SystemClock;
+import android.util.AtomicFile;
+
+import java.io.File;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * A simple base class for statistics that need to be saved/restored from a dedicated file. This
+ * class provides a base implementation that:
+ * <ul>
+ * <li>Provide an AtomicFile to the actual read/write code
+ * <li>A background-thread write and a synchronous write
+ * <li>Write-limiting for the background-thread (by default writes are at least 30 minutes apart)
+ * <li>Can lock on the provided data object before writing
+ * </ul>
+ * For completion, a subclass needs to implement actual {@link #writeInternal(Object) writing} and
+ * {@link #readInternal(Object) reading}.
+ */
+public abstract class AbstractStatsBase<T> {
+
+ private static final int WRITE_INTERVAL_MS =
+ (PackageManagerService.DEBUG_DEXOPT) ? 0 : 30*60*1000;
+ private final Object mFileLock = new Object();
+ private final AtomicLong mLastTimeWritten = new AtomicLong(0);
+ private final AtomicBoolean mBackgroundWriteRunning = new AtomicBoolean(false);
+ private final String mFileName;
+ private final String mBackgroundThreadName;
+ private final boolean mLock;
+
+ protected AbstractStatsBase(String fileName, String threadName, boolean lock) {
+ mFileName = fileName;
+ mBackgroundThreadName = threadName;
+ mLock = lock;
+ }
+
+ protected AtomicFile getFile() {
+ File dataDir = Environment.getDataDirectory();
+ File systemDir = new File(dataDir, "system");
+ File fname = new File(systemDir, mFileName);
+ return new AtomicFile(fname);
+ }
+
+ void writeNow(final T data) {
+ writeImpl(data);
+ mLastTimeWritten.set(SystemClock.elapsedRealtime());
+ }
+
+ boolean maybeWriteAsync(final T data) {
+ if (SystemClock.elapsedRealtime() - mLastTimeWritten.get() < WRITE_INTERVAL_MS
+ && !PackageManagerService.DEBUG_DEXOPT) {
+ return false;
+ }
+
+ if (mBackgroundWriteRunning.compareAndSet(false, true)) {
+ new Thread(mBackgroundThreadName) {
+ @Override
+ public void run() {
+ try {
+ writeImpl(data);
+ mLastTimeWritten.set(SystemClock.elapsedRealtime());
+ } finally {
+ mBackgroundWriteRunning.set(false);
+ }
+ }
+ }.start();
+ return true;
+ }
+
+ return false;
+ }
+
+ private void writeImpl(T data) {
+ if (mLock) {
+ synchronized (data) {
+ synchronized (mFileLock) {
+ writeInternal(data);
+ }
+ }
+ } else {
+ synchronized (mFileLock) {
+ writeInternal(data);
+ }
+ }
+ }
+
+ protected abstract void writeInternal(T data);
+
+ void read(T data) {
+ if (mLock) {
+ synchronized (data) {
+ synchronized (mFileLock) {
+ readInternal(data);
+ }
+ }
+ } else {
+ synchronized (mFileLock) {
+ readInternal(data);
+ }
+ }
+ // We use the current time as last-written. read() is called on system server startup
+ // (current situation), and we want to postpone I/O at boot.
+ mLastTimeWritten.set(SystemClock.elapsedRealtime());
+ }
+
+ protected abstract void readInternal(T data);
+}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 87f0581..cec1058 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -28,10 +28,13 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
+import android.os.Environment;
import android.os.ServiceManager;
+import android.os.storage.StorageManager;
import android.util.ArraySet;
import android.util.Log;
+import java.io.File;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.TimeUnit;
@@ -66,6 +69,8 @@
*/
final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false);
+ private final File dataDir = Environment.getDataDirectory();
+
public static void schedule(Context context) {
JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
@@ -113,6 +118,16 @@
return (100 * level / scale);
}
+ private long getLowStorageThreshold() {
+ @SuppressWarnings("deprecation")
+ final long lowThreshold = StorageManager.from(this).getStorageLowBytes(dataDir);
+ if (lowThreshold == 0) {
+ Log.e(TAG, "Invalid low storage threshold");
+ }
+
+ return lowThreshold;
+ }
+
private boolean runPostBootUpdate(final JobParameters jobParams,
final PackageManagerService pm, final ArraySet<String> pkgs) {
if (mExitPostBootUpdate.get()) {
@@ -124,6 +139,8 @@
final int lowBatteryThreshold = getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel);
+ final long lowThreshold = getLowStorageThreshold();
+
mAbortPostBootUpdate.set(false);
new Thread("BackgroundDexOptService_PostBootUpdate") {
@Override
@@ -141,6 +158,14 @@
// Rather bail than completely drain the battery.
break;
}
+ long usableSpace = dataDir.getUsableSpace();
+ if (usableSpace < lowThreshold) {
+ // Rather bail than completely fill up the disk.
+ Log.w(TAG, "Aborting background dex opt job due to low storage: " +
+ usableSpace);
+ break;
+ }
+
if (DEBUG_DEXOPT) {
Log.i(TAG, "Updating package " + pkg);
}
@@ -171,6 +196,9 @@
mExitPostBootUpdate.set(true);
mAbortIdleOptimization.set(false);
+
+ final long lowThreshold = getLowStorageThreshold();
+
new Thread("BackgroundDexOptService_IdleOptimization") {
@Override
public void run() {
@@ -183,6 +211,15 @@
// Skip previously failing package
continue;
}
+
+ long usableSpace = dataDir.getUsableSpace();
+ if (usableSpace < lowThreshold) {
+ // Rather bail than completely fill up the disk.
+ Log.w(TAG, "Aborting background dex opt job due to low storage: " +
+ usableSpace);
+ break;
+ }
+
// Conservatively add package to the list of failing ones in case performDexOpt
// never returns.
synchronized (sFailedPackageNames) {
@@ -213,6 +250,9 @@
Log.i(TAG, "onStartJob");
}
+ // NOTE: PackageManagerService.isStorageLow uses a different set of criteria from
+ // the checks above. This check is not "live" - the value is determined by a background
+ // restart with a period of ~1 minute.
PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
if (pm.isStorageLow()) {
if (DEBUG_DEXOPT) {
diff --git a/services/core/java/com/android/server/pm/CompilerStats.java b/services/core/java/com/android/server/pm/CompilerStats.java
new file mode 100644
index 0000000..8c2fc3e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/CompilerStats.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.Log;
+
+import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.IndentingPrintWriter;
+
+import libcore.io.IoUtils;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A class that collects, serializes and deserializes compiler-related statistics on a
+ * per-package per-code-path basis.
+ *
+ * Currently used to track compile times.
+ */
+class CompilerStats extends AbstractStatsBase<Void> {
+
+ private final static String COMPILER_STATS_VERSION_HEADER = "PACKAGE_MANAGER__COMPILER_STATS__";
+ private final static int COMPILER_STATS_VERSION = 1;
+
+ /**
+ * Class to collect all stats pertaining to one package.
+ */
+ static class PackageStats {
+
+ private final String packageName;
+
+ /**
+ * This map stores compile-times for all code paths in the package. The value
+ * is in milliseconds.
+ */
+ private final Map<String, Long> compileTimePerCodePath;
+
+ /**
+ * @param packageName
+ */
+ public PackageStats(String packageName) {
+ this.packageName = packageName;
+ // We expect at least one element in here, but let's make it minimal.
+ compileTimePerCodePath = new ArrayMap<>(2);
+ }
+
+ public String getPackageName() {
+ return packageName;
+ }
+
+ /**
+ * Return the recorded compile time for a given code path. Returns
+ * 0 if there is no recorded time.
+ */
+ public long getCompileTime(String codePath) {
+ String storagePath = getStoredPathFromCodePath(codePath);
+ synchronized (compileTimePerCodePath) {
+ Long l = compileTimePerCodePath.get(storagePath);
+ if (l == null) {
+ return 0;
+ }
+ return l;
+ }
+ }
+
+ public void setCompileTime(String codePath, long compileTimeInMs) {
+ String storagePath = getStoredPathFromCodePath(codePath);
+ synchronized (compileTimePerCodePath) {
+ if (compileTimeInMs <= 0) {
+ compileTimePerCodePath.remove(storagePath);
+ } else {
+ compileTimePerCodePath.put(storagePath, compileTimeInMs);
+ }
+ }
+ }
+
+ private static String getStoredPathFromCodePath(String codePath) {
+ int lastSlash = codePath.lastIndexOf(File.separatorChar);
+ return codePath.substring(lastSlash + 1);
+ }
+
+ public void dump(IndentingPrintWriter ipw) {
+ synchronized (compileTimePerCodePath) {
+ if (compileTimePerCodePath.size() == 0) {
+ ipw.println("(No recorded stats)");
+ } else {
+ for (Map.Entry<String, Long> e : compileTimePerCodePath.entrySet()) {
+ ipw.println(" " + e.getKey() + " - " + e.getValue());
+ }
+ }
+ }
+ }
+ }
+
+ private final Map<String, PackageStats> packageStats;
+
+ public CompilerStats() {
+ super("package-cstats.list", "CompilerStats_DiskWriter", /* lock */ false);
+ packageStats = new HashMap<>();
+ }
+
+ public PackageStats getPackageStats(String packageName) {
+ synchronized (packageStats) {
+ return packageStats.get(packageName);
+ }
+ }
+
+ public void setPackageStats(String packageName, PackageStats stats) {
+ synchronized (packageStats) {
+ packageStats.put(packageName, stats);
+ }
+ }
+
+ public PackageStats createPackageStats(String packageName) {
+ synchronized (packageStats) {
+ PackageStats newStats = new PackageStats(packageName);
+ packageStats.put(packageName, newStats);
+ return newStats;
+ }
+ }
+
+ public PackageStats getOrCreatePackageStats(String packageName) {
+ synchronized (packageStats) {
+ PackageStats existingStats = packageStats.get(packageName);
+ if (existingStats != null) {
+ return existingStats;
+ }
+
+ return createPackageStats(packageName);
+ }
+ }
+
+ public void deletePackageStats(String packageName) {
+ synchronized (packageStats) {
+ packageStats.remove(packageName);
+ }
+ }
+
+ // I/O
+
+ // The encoding is simple:
+ //
+ // 1) The first line is a line consisting of the version header and the version number.
+ //
+ // 2) The rest of the file is package data.
+ // 2.1) A package is started by any line not starting with "-";
+ // 2.2) Any line starting with "-" is code path data. The format is:
+ // '-'{code-path}':'{compile-time}
+
+ public void write(Writer out) {
+ @SuppressWarnings("resource")
+ FastPrintWriter fpw = new FastPrintWriter(out);
+
+ fpw.print(COMPILER_STATS_VERSION_HEADER);
+ fpw.println(COMPILER_STATS_VERSION);
+
+ synchronized (packageStats) {
+ for (PackageStats pkg : packageStats.values()) {
+ synchronized (pkg.compileTimePerCodePath) {
+ if (!pkg.compileTimePerCodePath.isEmpty()) {
+ fpw.println(pkg.getPackageName());
+
+ for (Map.Entry<String, Long> e : pkg.compileTimePerCodePath.entrySet()) {
+ fpw.println("-" + e.getKey() + ":" + e.getValue());
+ }
+ }
+ }
+ }
+ }
+
+ fpw.flush();
+ }
+
+ public boolean read(Reader r) {
+ synchronized (packageStats) {
+ // TODO: Could make this a final switch, then we wouldn't have to synchronize over
+ // the whole reading.
+ packageStats.clear();
+
+ try {
+ BufferedReader in = new BufferedReader(r);
+
+ // Read header, do version check.
+ String versionLine = in.readLine();
+ if (versionLine == null) {
+ throw new IllegalArgumentException("No version line found.");
+ } else {
+ if (!versionLine.startsWith(COMPILER_STATS_VERSION_HEADER)) {
+ throw new IllegalArgumentException("Invalid version line: " + versionLine);
+ }
+ int version = Integer.parseInt(
+ versionLine.substring(COMPILER_STATS_VERSION_HEADER.length()));
+ if (version != COMPILER_STATS_VERSION) {
+ // TODO: Upgrade older formats? For now, just reject and regenerate.
+ throw new IllegalArgumentException("Unexpected version: " + version);
+ }
+ }
+
+ // For simpler code, we ignore any data lines before the first package. We
+ // collect it in a fake package.
+ PackageStats currentPackage = new PackageStats("fake package");
+
+ String s = null;
+ while ((s = in.readLine()) != null) {
+ if (s.startsWith("-")) {
+ int colonIndex = s.indexOf(':');
+ if (colonIndex == -1 || colonIndex == 1) {
+ throw new IllegalArgumentException("Could not parse data " + s);
+ }
+ String codePath = s.substring(1, colonIndex);
+ long time = Long.parseLong(s.substring(colonIndex + 1));
+ currentPackage.setCompileTime(codePath, time);
+ } else {
+ currentPackage = getOrCreatePackageStats(s);
+ }
+ }
+ } catch (Exception e) {
+ Log.e(PackageManagerService.TAG, "Error parsing compiler stats", e);
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ void writeNow() {
+ writeNow(null);
+ }
+
+ boolean maybeWriteAsync() {
+ return maybeWriteAsync(null);
+ }
+
+ @Override
+ protected void writeInternal(Void data) {
+ AtomicFile file = getFile();
+ FileOutputStream f = null;
+
+ try {
+ f = file.startWrite();
+ OutputStreamWriter osw = new OutputStreamWriter(f);
+ write(osw);
+ osw.flush();
+ file.finishWrite(f);
+ } catch (IOException e) {
+ if (f != null) {
+ file.failWrite(f);
+ }
+ Log.e(PackageManagerService.TAG, "Failed to write compiler stats", e);
+ }
+ }
+
+ void read() {
+ read((Void)null);
+ }
+
+ @Override
+ protected void readInternal(Void data) {
+ AtomicFile file = getFile();
+ BufferedReader in = null;
+ try {
+ in = new BufferedReader(new InputStreamReader(file.openRead()));
+ read(in);
+ } catch (FileNotFoundException expected) {
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index 098b39e..d1dbdd8 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import android.Manifest;
+import android.annotation.NonNull;
import android.app.DownloadManager;
import android.app.admin.DevicePolicyManager;
import android.content.Intent;
@@ -30,7 +31,11 @@
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Build;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Message;
import android.os.UserHandle;
+import android.os.storage.StorageManager;
import android.print.PrintManager;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
@@ -38,12 +43,23 @@
import android.provider.Telephony.Sms.Intents;
import android.telephony.TelephonyManager;
import android.security.Credentials;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Slog;
+import android.util.Xml;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import java.io.BufferedInputStream;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import static android.os.Process.FIRST_APPLICATION_UID;
@@ -64,6 +80,13 @@
private static final String AUDIO_MIME_TYPE = "audio/mpeg";
+ private static final String TAG_EXCEPTIONS = "exceptions";
+ private static final String TAG_EXCEPTION = "exception";
+ private static final String TAG_PERMISSION = "permission";
+ private static final String ATTR_PACKAGE = "package";
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_FIXED = "fixed";
+
private static final Set<String> PHONE_PERMISSIONS = new ArraySet<>();
static {
PHONE_PERMISSIONS.add(Manifest.permission.READ_PHONE_STATE);
@@ -125,7 +148,10 @@
STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
+ private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1;
+
private final PackageManagerService mService;
+ private final Handler mHandler;
private PackagesProvider mLocationPackagesProvider;
private PackagesProvider mVoiceInteractionPackagesProvider;
@@ -134,8 +160,22 @@
private PackagesProvider mSimCallManagerPackagesProvider;
private SyncAdapterPackagesProvider mSyncAdapterPackagesProvider;
+ private ArrayMap<String, List<DefaultPermissionGrant>> mGrantExceptions;
+
public DefaultPermissionGrantPolicy(PackageManagerService service) {
mService = service;
+ mHandler = new Handler(mService.mHandlerThread.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS) {
+ synchronized (mService.mPackages) {
+ if (mGrantExceptions == null) {
+ mGrantExceptions = readDefaultPermissionExceptionsLPw();
+ }
+ }
+ }
+ }
+ };
}
public void setLocationPackagesProviderLPw(PackagesProvider provider) {
@@ -165,6 +205,11 @@
public void grantDefaultPermissions(int userId) {
grantPermissionsToSysComponentsAndPrivApps(userId);
grantDefaultSystemHandlerPermissions(userId);
+ grantDefaultPermissionExceptions(userId);
+ }
+
+ public void scheduleReadDefaultPermissionExceptions() {
+ mHandler.sendEmptyMessage(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS);
}
private void grantPermissionsToSysComponentsAndPrivApps(int userId) {
@@ -473,6 +518,7 @@
if (emailPackage != null
&& doesPackageSupportRuntimePermissions(emailPackage)) {
grantRuntimePermissionsLPw(emailPackage, CONTACTS_PERMISSIONS, userId);
+ grantRuntimePermissionsLPw(emailPackage, CALENDAR_PERMISSIONS, userId);
}
// Browser
@@ -605,6 +651,15 @@
grantRuntimePermissionsLPw(nfcTagPkg, CONTACTS_PERMISSIONS, false, userId);
grantRuntimePermissionsLPw(nfcTagPkg, PHONE_PERMISSIONS, false, userId);
}
+
+ // Storage Manager
+ Intent storageManagerIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
+ PackageParser.Package storageManagerPckg = getDefaultSystemHandlerActivityPackageLPr(
+ storageManagerIntent, userId);
+ if (storageManagerPckg != null
+ && doesPackageSupportRuntimePermissions(storageManagerPckg)) {
+ grantRuntimePermissionsLPw(storageManagerPckg, STORAGE_PERMISSIONS, true, userId);
+ }
mService.mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);
}
}
@@ -619,6 +674,7 @@
grantRuntimePermissionsLPw(dialerPackage, CONTACTS_PERMISSIONS, userId);
grantRuntimePermissionsLPw(dialerPackage, SMS_PERMISSIONS, userId);
grantRuntimePermissionsLPw(dialerPackage, MICROPHONE_PERMISSIONS, userId);
+ grantRuntimePermissionsLPw(dialerPackage, CAMERA_PERMISSIONS, userId);
}
}
@@ -656,6 +712,7 @@
grantRuntimePermissionsLPw(dialerPackage, CONTACTS_PERMISSIONS, false, true, userId);
grantRuntimePermissionsLPw(dialerPackage, SMS_PERMISSIONS, false, true, userId);
grantRuntimePermissionsLPw(dialerPackage, MICROPHONE_PERMISSIONS, false, true, userId);
+ grantRuntimePermissionsLPw(dialerPackage, CAMERA_PERMISSIONS, false, true, userId);
}
}
@@ -903,7 +960,175 @@
pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
}
+ private void grantDefaultPermissionExceptions(int userId) {
+ synchronized (mService.mPackages) {
+ mHandler.removeMessages(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS);
+
+ if (mGrantExceptions == null) {
+ mGrantExceptions = readDefaultPermissionExceptionsLPw();
+ }
+
+ // mGrantExceptions is null only before the first read and then
+ // it serves as a cache of the default grants that should be
+ // performed for every user. If there is an entry then the app
+ // is on the system image and supports runtime permissions.
+ Set<String> permissions = null;
+ final int exceptionCount = mGrantExceptions.size();
+ for (int i = 0; i < exceptionCount; i++) {
+ String packageName = mGrantExceptions.keyAt(i);
+ PackageParser.Package pkg = getSystemPackageLPr(packageName);
+ List<DefaultPermissionGrant> permissionGrants = mGrantExceptions.valueAt(i);
+ final int permissionGrantCount = permissionGrants.size();
+ for (int j = 0; j < permissionGrantCount; j++) {
+ DefaultPermissionGrant permissionGrant = permissionGrants.get(j);
+ if (permissions == null) {
+ permissions = new ArraySet<>();
+ } else {
+ permissions.clear();
+ }
+ permissions.add(permissionGrant.name);
+ grantRuntimePermissionsLPw(pkg, permissions, false,
+ permissionGrant.fixed, userId);
+ }
+ }
+ }
+ }
+
+ private @NonNull ArrayMap<String, List<DefaultPermissionGrant>>
+ readDefaultPermissionExceptionsLPw() {
+ File dir = new File(Environment.getRootDirectory(), "etc/default-permissions");
+ if (!dir.exists() || !dir.isDirectory() || !dir.canRead()) {
+ return new ArrayMap<>(0);
+ }
+
+ File[] files = dir.listFiles();
+ if (files == null) {
+ return new ArrayMap<>(0);
+ }
+
+ ArrayMap<String, List<DefaultPermissionGrant>> grantExceptions = new ArrayMap<>();
+
+ // Iterate over the files in the directory and scan .xml files
+ for (File file : files) {
+ if (!file.getPath().endsWith(".xml")) {
+ Slog.i(TAG, "Non-xml file " + file + " in " + dir + " directory, ignoring");
+ continue;
+ }
+ if (!file.canRead()) {
+ Slog.w(TAG, "Default permissions file " + file + " cannot be read");
+ continue;
+ }
+ try (
+ InputStream str = new BufferedInputStream(new FileInputStream(file))
+ ) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(str, null);
+ parse(parser, grantExceptions);
+ } catch (XmlPullParserException | IOException e) {
+ Slog.w(TAG, "Error reading default permissions file " + file, e);
+ }
+ }
+
+ return grantExceptions;
+ }
+
+ private void parse(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>>
+ outGrantExceptions) throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ if (TAG_EXCEPTIONS.equals(parser.getName())) {
+ parseExceptions(parser, outGrantExceptions);
+ } else {
+ Log.e(TAG, "Unknown tag " + parser.getName());
+ }
+ }
+ }
+
+ private void parseExceptions(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>>
+ outGrantExceptions) throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ if (TAG_EXCEPTION.equals(parser.getName())) {
+ String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
+
+ List<DefaultPermissionGrant> packageExceptions =
+ outGrantExceptions.get(packageName);
+ if (packageExceptions == null) {
+ // The package must be on the system image
+ PackageParser.Package pkg = getSystemPackageLPr(packageName);
+ if (pkg == null) {
+ Log.w(TAG, "Unknown package:" + packageName);
+ XmlUtils.skipCurrentTag(parser);
+ return;
+ }
+
+ // The package must support runtime permissions
+ if (!doesPackageSupportRuntimePermissions(pkg)) {
+ Log.w(TAG, "Skipping non supporting runtime permissions package:"
+ + packageName);
+ XmlUtils.skipCurrentTag(parser);
+ return;
+ }
+ packageExceptions = new ArrayList<>();
+ outGrantExceptions.put(packageName, packageExceptions);
+ }
+
+ parsePermission(parser, packageExceptions);
+ } else {
+ Log.e(TAG, "Unknown tag " + parser.getName() + "under <exceptions>");
+ }
+ }
+ }
+
+ private void parsePermission(XmlPullParser parser, List<DefaultPermissionGrant>
+ outPackageExceptions) throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ if (TAG_PERMISSION.contains(parser.getName())) {
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ if (name == null) {
+ Log.w(TAG, "Mandatory name attribute missing for permission tag");
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+
+ final boolean fixed = XmlUtils.readBooleanAttribute(parser, ATTR_FIXED);
+
+ DefaultPermissionGrant exception = new DefaultPermissionGrant(name, fixed);
+ outPackageExceptions.add(exception);
+ } else {
+ Log.e(TAG, "Unknown tag " + parser.getName() + "under <exception>");
+ }
+ }
+ }
+
private static boolean doesPackageSupportRuntimePermissions(PackageParser.Package pkg) {
return pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
}
+
+ private static final class DefaultPermissionGrant {
+ final String name;
+ final boolean fixed;
+
+ public DefaultPermissionGrant(String name, boolean fixed) {
+ this.name = name;
+ this.fixed = fixed;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
index fe6fb1f..8d926f5 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
+++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
@@ -16,6 +16,8 @@
package com.android.server.pm;
+import android.app.EphemeralResolverService;
+import android.app.IEphemeralResolver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -30,9 +32,6 @@
import android.os.UserHandle;
import android.util.TimedRemoteCaller;
-import com.android.internal.app.EphemeralResolverService;
-import com.android.internal.app.IEphemeralResolver;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -64,11 +63,12 @@
mIntent = new Intent().setComponent(componentName);
}
- public final List<EphemeralResolveInfo> getEphemeralResolveInfoList(int hashPrefix) {
+ public final List<EphemeralResolveInfo> getEphemeralResolveInfoList(
+ int hashPrefix[], int prefixMask) {
throwIfCalledOnMainThread();
try {
return mGetEphemeralResolveInfoCaller.getEphemeralResolveInfoList(
- getRemoteInstanceLazy(), hashPrefix);
+ getRemoteInstanceLazy(), hashPrefix, prefixMask);
} catch (RemoteException re) {
} catch (TimeoutException te) {
} finally {
@@ -177,10 +177,10 @@
}
public List<EphemeralResolveInfo> getEphemeralResolveInfoList(
- IEphemeralResolver target, int hashPrefix)
+ IEphemeralResolver target, int hashPrefix[], int prefixMask)
throws RemoteException, TimeoutException {
final int sequence = onBeforeRemoteCall();
- target.getEphemeralResolveInfoList(mCallback, hashPrefix, sequence);
+ target.getEphemeralResolveInfoList(mCallback, hashPrefix, prefixMask, sequence);
return getResultTimed(sequence);
}
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 7b85a4f..72c549f 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -61,6 +61,13 @@
mInstaller = new InstallerConnection();
}
+ // Package-private installer that accepts a custom InstallerConnection. Used for
+ // OtaDexoptService.
+ Installer(Context context, InstallerConnection connection) {
+ super(context);
+ mInstaller = connection;
+ }
+
/**
* Yell loudly if someone tries making future calls while holding a lock on
* the given object.
diff --git a/services/core/java/com/android/server/pm/IntentFilterVerificationState.java b/services/core/java/com/android/server/pm/IntentFilterVerificationState.java
index c6e7911..a4e9d10 100644
--- a/services/core/java/com/android/server/pm/IntentFilterVerificationState.java
+++ b/services/core/java/com/android/server/pm/IntentFilterVerificationState.java
@@ -96,7 +96,12 @@
if (i > 0) {
sb.append(" ");
}
- sb.append(mHosts.valueAt(i));
+ String host = mHosts.valueAt(i);
+ // "*.example.tld" is validated via https://example.tld
+ if (host.startsWith("*.")) {
+ host = host.substring(2);
+ }
+ sb.append(host);
}
return sb.toString();
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index ffe8f75..53e328c 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -17,8 +17,9 @@
package com.android.server.pm;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Context;
@@ -98,6 +99,7 @@
private final Context mContext;
private final PackageManager mPm;
private final UserManager mUm;
+ private final ActivityManagerInternal mActivityManagerInternal;
private final ShortcutServiceInternal mShortcutServiceInternal;
private final PackageCallbackList<IOnAppsChangedListener> mListeners
= new PackageCallbackList<IOnAppsChangedListener>();
@@ -110,6 +112,8 @@
mContext = context;
mPm = mContext.getPackageManager();
mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mActivityManagerInternal = Preconditions.checkNotNull(
+ LocalServices.getService(ActivityManagerInternal.class));
mShortcutServiceInternal = Preconditions.checkNotNull(
LocalServices.getService(ShortcutServiceInternal.class));
mShortcutServiceInternal.addListener(mPackageMonitor);
@@ -380,7 +384,8 @@
"To query by shortcut ID, package name must also be set");
}
- return new ParceledListSlice<>(
+ // TODO(b/29399275): Eclipse compiler requires explicit List<ShortcutInfo> cast below.
+ return new ParceledListSlice<>((List<ShortcutInfo>)
mShortcutServiceInternal.getShortcuts(getCallingUserId(),
callingPackage, changedSince, packageName, shortcutIds,
componentName, flags, user.getIdentifier()));
@@ -447,23 +452,41 @@
ensureShortcutPermission(callingPackage, userId);
}
- final Intent intent = mShortcutServiceInternal.createShortcutIntent(getCallingUserId(),
- callingPackage, packageName, shortcutId, userId);
- if (intent == null) {
+ final Intent[] intents = mShortcutServiceInternal.createShortcutIntents(
+ getCallingUserId(), callingPackage, packageName, shortcutId, userId);
+ if (intents == null || intents.length == 0) {
return false;
}
// Note the target activity doesn't have to be exported.
- intent.setSourceBounds(sourceBounds);
- prepareIntentForLaunch(intent, sourceBounds);
+ intents[0].addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intents[0].setSourceBounds(sourceBounds);
- final long ident = Binder.clearCallingIdentity();
+ return startShortcutIntentsAsPublisher(
+ intents, packageName, startActivityOptions, userId);
+ }
+
+ private boolean startShortcutIntentsAsPublisher(@NonNull Intent[] intents,
+ @NonNull String publisherPackage, Bundle startActivityOptions, int userId) {
+ final int code;
+ final long ident = injectClearCallingIdentity();
try {
- mContext.startActivityAsUser(intent, startActivityOptions, UserHandle.of(userId));
+ code = mActivityManagerInternal.startActivitiesAsPackage(publisherPackage,
+ userId, intents, startActivityOptions);
+ if (code >= ActivityManager.START_SUCCESS) {
+ return true; // Success
+ } else {
+ Log.e(TAG, "Couldn't start activity, code=" + code);
+ }
+ return code >= ActivityManager.START_SUCCESS;
+ } catch (SecurityException e) {
+ if (DEBUG) {
+ Slog.d(TAG, "SecurityException while launching intent", e);
+ }
+ return false;
} finally {
- Binder.restoreCallingIdentity(ident);
+ injectRestoreCallingIdentity(ident);
}
- return true;
}
@Override
@@ -497,7 +520,9 @@
Intent launchIntent = new Intent(Intent.ACTION_MAIN);
launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- prepareIntentForLaunch(launchIntent, sourceBounds);
+ launchIntent.setSourceBounds(sourceBounds);
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
launchIntent.setPackage(component.getPackageName());
long ident = Binder.clearCallingIdentity();
@@ -538,13 +563,6 @@
}
}
- private void prepareIntentForLaunch(@NonNull Intent launchIntent,
- @Nullable Rect sourceBounds) {
- launchIntent.setSourceBounds(sourceBounds);
- launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- }
-
@Override
public void showAppDetailsAsUser(ComponentName component, Rect sourceBounds,
Bundle opts, UserHandle user) throws RemoteException {
@@ -745,50 +763,51 @@
@Override
public void onShortcutChanged(@NonNull String packageName,
@UserIdInt int userId) {
- if (!ShortcutService.FEATURE_ENABLED) {
- return;
- }
postToPackageMonitorHandler(() -> onShortcutChangedInner(packageName, userId));
}
private void onShortcutChangedInner(@NonNull String packageName,
@UserIdInt int userId) {
- final UserHandle user = UserHandle.of(userId);
+ try {
+ final UserHandle user = UserHandle.of(userId);
- final int n = mListeners.beginBroadcast();
- for (int i = 0; i < n; i++) {
- IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
- BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(user, cookie.user, "onShortcutChanged")) continue;
+ final int n = mListeners.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+ BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
+ if (!isEnabledProfileOf(user, cookie.user, "onShortcutChanged")) continue;
- final int launcherUserId = cookie.user.getIdentifier();
+ final int launcherUserId = cookie.user.getIdentifier();
- // Make sure the caller has the permission.
- if (!mShortcutServiceInternal.hasShortcutHostPermission(
- launcherUserId, cookie.packageName)) {
- continue;
+ // Make sure the caller has the permission.
+ if (!mShortcutServiceInternal.hasShortcutHostPermission(
+ launcherUserId, cookie.packageName)) {
+ continue;
+ }
+ // Each launcher has a different set of pinned shortcuts, so we need to do a
+ // query in here.
+ // (As of now, only one launcher has the permission at a time, so it's bit
+ // moot, but we may change the permission model eventually.)
+ final List<ShortcutInfo> list =
+ mShortcutServiceInternal.getShortcuts(launcherUserId,
+ cookie.packageName,
+ /* changedSince= */ 0, packageName, /* shortcutIds=*/ null,
+ /* component= */ null,
+ ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY
+ | ShortcutQuery.FLAG_GET_ALL_KINDS
+ , userId);
+ try {
+ listener.onShortcutChanged(user, packageName,
+ new ParceledListSlice<>(list));
+ } catch (RemoteException re) {
+ Slog.d(TAG, "Callback failed ", re);
+ }
}
- // Each launcher has a different set of pinned shortcuts, so we need to do a
- // query in here.
- // (As of now, only one launcher has the permission at a time, so it's bit
- // moot, but we may change the permission model eventually.)
- final List<ShortcutInfo> list =
- mShortcutServiceInternal.getShortcuts(launcherUserId,
- cookie.packageName,
- /* changedSince= */ 0, packageName, /* shortcutIds=*/ null,
- /* component= */ null,
- ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY
- | ShortcutQuery.FLAG_GET_PINNED
- | ShortcutQuery.FLAG_GET_DYNAMIC
- , userId);
- try {
- listener.onShortcutChanged(user, packageName,
- new ParceledListSlice<>(list));
- } catch (RemoteException re) {
- Slog.d(TAG, "Callback failed ", re);
- }
+ mListeners.finishBroadcast();
+ } catch (RuntimeException e) {
+ // When the user is locked we get IllegalState, so just catch all.
+ Log.w(TAG, e.getMessage(), e);
}
- mListeners.finishBroadcast();
}
}
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 649a27c..bff6d2d 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -32,10 +32,12 @@
import android.util.Log;
import android.util.Slog;
+import com.android.internal.os.InstallerConnection;
import com.android.internal.os.InstallerConnection.InstallerException;
import java.io.File;
import java.io.FileDescriptor;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -48,21 +50,25 @@
private final static String TAG = "OTADexopt";
private final static boolean DEBUG_DEXOPT = true;
+ // The synthetic library dependencies denoting "no checks."
+ private final static String[] NO_LIBRARIES = new String[] { "&" };
+
private final Context mContext;
- private final PackageDexOptimizer mPackageDexOptimizer;
private final PackageManagerService mPackageManagerService;
// TODO: Evaluate the need for WeakReferences here.
- private List<PackageParser.Package> mDexoptPackages;
+
+ /**
+ * The list of dexopt invocations for all work.
+ */
+ private List<String> mDexoptCommands;
+
+ private int completeSize;
public OtaDexoptService(Context context, PackageManagerService packageManagerService) {
this.mContext = context;
this.mPackageManagerService = packageManagerService;
- // Use the package manager install and install lock here for the OTA dex optimizer.
- mPackageDexOptimizer = new OTADexoptPackageDexOptimizer(packageManagerService.mInstaller,
- packageManagerService.mInstallLock, context);
-
// Now it's time to check whether we need to move any A/B artifacts.
moveAbArtifacts(packageManagerService.mInstaller);
}
@@ -84,13 +90,44 @@
@Override
public synchronized void prepare() throws RemoteException {
- if (mDexoptPackages != null) {
+ if (mDexoptCommands != null) {
throw new IllegalStateException("already called prepare()");
}
+ final List<PackageParser.Package> important;
+ final List<PackageParser.Package> others;
synchronized (mPackageManagerService.mPackages) {
- mDexoptPackages = PackageManagerServiceUtils.getPackagesForDexopt(
+ // Important: the packages we need to run with ab-ota compiler-reason.
+ important = PackageManagerServiceUtils.getPackagesForDexopt(
mPackageManagerService.mPackages.values(), mPackageManagerService);
+ // Others: we should optimize this with the (first-)boot compiler-reason.
+ others = new ArrayList<>(mPackageManagerService.mPackages.values());
+ others.removeAll(important);
+
+ // Pre-size the array list by over-allocating by a factor of 1.5.
+ mDexoptCommands = new ArrayList<>(3 * mPackageManagerService.mPackages.size() / 2);
}
+
+ for (PackageParser.Package p : important) {
+ // Make sure that core apps are optimized according to their own "reason".
+ // If the core apps are not preopted in the B OTA, and REASON_AB_OTA is not speed
+ // (by default is speed-profile) they will be interepreted/JITed. This in itself is
+ // not a problem as we will end up doing profile guided compilation. However, some
+ // core apps may be loaded by system server which doesn't JIT and we need to make
+ // sure we don't interpret-only
+ int compilationReason = p.coreApp
+ ? PackageManagerService.REASON_CORE_APP
+ : PackageManagerService.REASON_AB_OTA;
+ mDexoptCommands.addAll(generatePackageDexopts(p, compilationReason));
+ }
+ for (PackageParser.Package p : others) {
+ // We assume here that there are no core apps left.
+ if (p.coreApp) {
+ throw new IllegalStateException("Found a core app that's not important");
+ }
+ mDexoptCommands.addAll(
+ generatePackageDexopts(p, PackageManagerService.REASON_FIRST_BOOT));
+ }
+ completeSize = mDexoptCommands.size();
}
@Override
@@ -98,35 +135,52 @@
if (DEBUG_DEXOPT) {
Log.i(TAG, "Cleaning up OTA Dexopt state.");
}
- mDexoptPackages = null;
+ mDexoptCommands = null;
}
@Override
public synchronized boolean isDone() throws RemoteException {
- if (mDexoptPackages == null) {
+ if (mDexoptCommands == null) {
throw new IllegalStateException("done() called before prepare()");
}
- return mDexoptPackages.isEmpty();
+ return mDexoptCommands.isEmpty();
}
@Override
- public synchronized void dexoptNextPackage() throws RemoteException {
- if (mDexoptPackages == null) {
+ public synchronized float getProgress() throws RemoteException {
+ // Approximate the progress by the amount of already completed commands.
+ if (completeSize == 0) {
+ return 1f;
+ }
+ int commandsLeft = mDexoptCommands.size();
+ return (completeSize - commandsLeft) / ((float)completeSize);
+ }
+
+ @Override
+ public synchronized String nextDexoptCommand() throws RemoteException {
+ if (mDexoptCommands == null) {
throw new IllegalStateException("dexoptNextPackage() called before prepare()");
}
- if (mDexoptPackages.isEmpty()) {
- // Tolerate repeated calls.
- return;
+
+ if (mDexoptCommands.isEmpty()) {
+ return "(all done)";
}
- PackageParser.Package nextPackage = mDexoptPackages.remove(0);
+ String next = mDexoptCommands.remove(0);
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Processing " + nextPackage.packageName + " for OTA dexopt.");
+ if (IsFreeSpaceAvailable()) {
+ return next;
+ } else {
+ mDexoptCommands.clear();
+ return "(no free space)";
}
+ }
- // Check for low space.
+ /**
+ * Check for low space. Returns true if there's space left.
+ */
+ private boolean IsFreeSpaceAvailable() {
// TODO: If apps are not installed in the internal /data partition, we should compare
// against that storage's free capacity.
File dataDir = Environment.getDataDirectory();
@@ -136,19 +190,43 @@
throw new IllegalStateException("Invalid low memory threshold");
}
long usableSpace = dataDir.getUsableSpace();
- if (usableSpace < lowThreshold) {
- Log.w(TAG, "Not running dexopt on " + nextPackage.packageName + " due to low memory: " +
- usableSpace);
- return;
+ return (usableSpace >= lowThreshold);
+ }
+
+ /**
+ * Generate all dexopt commands for the given package.
+ */
+ private synchronized List<String> generatePackageDexopts(PackageParser.Package pkg,
+ int compilationReason) {
+ // Use our custom connection that just collects the commands.
+ RecordingInstallerConnection collectingConnection = new RecordingInstallerConnection();
+ Installer collectingInstaller = new Installer(mContext, collectingConnection);
+
+ // Use the package manager install and install lock here for the OTA dex optimizer.
+ PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
+ collectingInstaller, mPackageManagerService.mInstallLock, mContext);
+
+ String[] libraryDependencies = pkg.usesLibraryFiles;
+ if (pkg.isSystemApp()) {
+ // For system apps, we want to avoid classpaths checks.
+ libraryDependencies = NO_LIBRARIES;
}
- mPackageDexOptimizer.performDexOpt(nextPackage, nextPackage.usesLibraryFiles,
+ optimizer.performDexOpt(pkg, libraryDependencies,
null /* ISAs */, false /* checkProfiles */,
- getCompilerFilterForReason(PackageManagerService.REASON_AB_OTA));
+ getCompilerFilterForReason(compilationReason),
+ null /* CompilerStats.PackageStats */);
+
+ return collectingConnection.commands;
+ }
+
+ @Override
+ public synchronized void dexoptNextPackage() throws RemoteException {
+ throw new UnsupportedOperationException();
}
private void moveAbArtifacts(Installer installer) {
- if (mDexoptPackages != null) {
+ if (mDexoptCommands != null) {
throw new IllegalStateException("Should not be ota-dexopting when trying to move.");
}
@@ -208,4 +286,40 @@
}
}
+
+ private static class RecordingInstallerConnection extends InstallerConnection {
+ public List<String> commands = new ArrayList<String>(1);
+
+ @Override
+ public void setWarnIfHeld(Object warnIfHeld) {
+ throw new IllegalStateException("Should not reach here");
+ }
+
+ @Override
+ public synchronized String transact(String cmd) {
+ commands.add(cmd);
+ return "0";
+ }
+
+ @Override
+ public boolean mergeProfiles(int uid, String pkgName) throws InstallerException {
+ throw new IllegalStateException("Should not reach here");
+ }
+
+ @Override
+ public boolean dumpProfiles(String gid, String packageName, String codePaths)
+ throws InstallerException {
+ throw new IllegalStateException("Should not reach here");
+ }
+
+ @Override
+ public void disconnect() {
+ throw new IllegalStateException("Should not reach here");
+ }
+
+ @Override
+ public void waitForConnection() {
+ throw new IllegalStateException("Should not reach here");
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java b/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
index ea9cf17..bbd4048 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
@@ -46,6 +46,10 @@
return runOtaDone();
case "step":
return runOtaStep();
+ case "next":
+ return runOtaNext();
+ case "progress":
+ return runOtaProgress();
default:
return handleDefaultCommands(cmd);
}
@@ -81,6 +85,18 @@
return 0;
}
+ private int runOtaNext() throws RemoteException {
+ getOutPrintWriter().println(mInterface.nextDexoptCommand());
+ return 0;
+ }
+
+ private int runOtaProgress() throws RemoteException {
+ final float progress = mInterface.getProgress();
+ final PrintWriter pw = getOutPrintWriter();
+ pw.format("%.2f", progress);
+ return 0;
+ }
+
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
@@ -94,6 +110,8 @@
pw.println(" Replies whether the OTA is complete or not.");
pw.println(" step");
pw.println(" OTA dexopt the next package.");
+ pw.println(" next");
+ pw.println(" Get parameters for OTA dexopt of the next package.");
pw.println(" cleanup");
pw.println(" Clean up internal states. Ends an OTA session.");
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 26a840d..19b1201 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -90,7 +90,8 @@
* synchronized on {@link #mInstallLock}.
*/
int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries,
- String[] instructionSets, boolean checkProfiles, String targetCompilationFilter) {
+ String[] instructionSets, boolean checkProfiles, String targetCompilationFilter,
+ CompilerStats.PackageStats packageStats) {
synchronized (mInstallLock) {
final boolean useLock = mSystemReady;
if (useLock) {
@@ -99,7 +100,7 @@
}
try {
return performDexOptLI(pkg, sharedLibraries, instructionSets, checkProfiles,
- targetCompilationFilter);
+ targetCompilationFilter, packageStats);
} finally {
if (useLock) {
mDexoptWakeLock.release();
@@ -150,7 +151,8 @@
}
private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,
- String[] targetInstructionSets, boolean checkProfiles, String targetCompilerFilter) {
+ String[] targetInstructionSets, boolean checkProfiles, String targetCompilerFilter,
+ CompilerStats.PackageStats packageStats) {
final String[] instructionSets = targetInstructionSets != null ?
targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
@@ -254,10 +256,17 @@
| DEXOPT_BOOTCOMPLETE);
try {
+ long startTime = System.currentTimeMillis();
+
mInstaller.dexopt(path, sharedGid, pkg.packageName, dexCodeInstructionSet,
dexoptNeeded, oatDir, dexFlags, targetCompilerFilter, pkg.volumeUuid,
sharedLibrariesPath);
performedDexOpt = true;
+
+ if (packageStats != null) {
+ long endTime = System.currentTimeMillis();
+ packageStats.setCompileTime(path, (int)(endTime - startTime));
+ }
} catch (InstallerException e) {
Slog.w(TAG, "Failed to dexopt", e);
successfulDexOpt = false;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 83af017..6a56fa6 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -182,6 +182,10 @@
*/
private final Random mRandom = new SecureRandom();
+ /** All sessions allocated */
+ @GuardedBy("mSessions")
+ private final SparseBooleanArray mAllocatedSessions = new SparseBooleanArray();
+
@GuardedBy("mSessions")
private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
@@ -365,6 +369,7 @@
// keep details around for dumpsys.
mHistoricalSessions.put(session.sessionId, session);
}
+ mAllocatedSessions.put(session.sessionId, true);
}
}
}
@@ -666,23 +671,26 @@
"Too many historical sessions for UID " + callingUid);
}
- final long createdMillis = System.currentTimeMillis();
sessionId = allocateSessionIdLocked();
+ }
- // We're staging to exactly one location
- File stageDir = null;
- String stageCid = null;
- if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
- final boolean isEphemeral =
- (params.installFlags & PackageManager.INSTALL_EPHEMERAL) != 0;
- stageDir = buildStageDir(params.volumeUuid, sessionId, isEphemeral);
- } else {
- stageCid = buildExternalStageCid(sessionId);
- }
+ final long createdMillis = System.currentTimeMillis();
+ // We're staging to exactly one location
+ File stageDir = null;
+ String stageCid = null;
+ if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
+ final boolean isEphemeral =
+ (params.installFlags & PackageManager.INSTALL_EPHEMERAL) != 0;
+ stageDir = buildStageDir(params.volumeUuid, sessionId, isEphemeral);
+ } else {
+ stageCid = buildExternalStageCid(sessionId);
+ }
- session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
- mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
- params, createdMillis, stageDir, stageCid, false, false);
+ session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
+ mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
+ params, createdMillis, stageDir, stageCid, false, false);
+
+ synchronized (mSessions) {
mSessions.put(sessionId, session);
}
@@ -765,8 +773,8 @@
int sessionId;
do {
sessionId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
- if (mSessions.get(sessionId) == null && mHistoricalSessions.get(sessionId) == null
- && !mLegacySessions.get(sessionId, false)) {
+ if (!mAllocatedSessions.get(sessionId, false)) {
+ mAllocatedSessions.put(sessionId, true);
return sessionId;
}
} while (n++ < 32);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0019973..f326555 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -35,7 +35,6 @@
import static android.content.pm.PackageManager.INSTALL_EXTERNAL;
import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
import static android.content.pm.PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER;
-import static android.content.pm.PackageManager.INSTALL_FAILED_DEXOPT;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
import static android.content.pm.PackageManager.INSTALL_FAILED_EPHEMERAL_INVALID;
@@ -76,8 +75,6 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.PackageParser.PARSE_IS_PRIVILEGED;
import static android.content.pm.PackageParser.isApkFile;
-import static android.os.Process.PACKAGE_INFO_GID;
-import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDWR;
@@ -95,6 +92,7 @@
import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
import static com.android.server.pm.PackageManagerServiceCompilerMapping.getFullCompilerFilter;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getNonProfileGuidedCompilerFilter;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_FAILURE;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
@@ -102,7 +100,6 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
@@ -125,6 +122,7 @@
import android.content.pm.ComponentInfo;
import android.content.pm.EphemeralApplicationInfo;
import android.content.pm.EphemeralResolveInfo;
+import android.content.pm.EphemeralResolveInfo.EphemeralDigest;
import android.content.pm.EphemeralResolveInfo.EphemeralResolveIntentInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IOnPermissionsChangeListener;
@@ -197,6 +195,7 @@
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
+import android.provider.Settings.Global;
import android.security.KeyStore;
import android.security.SystemKeyStore;
import android.system.ErrnoException;
@@ -205,7 +204,6 @@
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.AtomicFile;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.ExceptionUtils;
@@ -264,7 +262,6 @@
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
-import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
@@ -277,7 +274,6 @@
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
-import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.security.DigestInputStream;
@@ -304,7 +300,6 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
/**
* Keep track of all those APKs everywhere.
@@ -362,13 +357,13 @@
static final boolean DEBUG_DEXOPT = false;
private static final boolean DEBUG_ABI_SELECTION = false;
- private static final boolean DEBUG_EPHEMERAL = false;
+ private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE;
private static final boolean DEBUG_TRIAGED_MISSING = false;
private static final boolean DEBUG_APP_DATA = false;
static final boolean CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE = false;
- private static final boolean DISABLE_EPHEMERAL_APPS = true;
+ private static final boolean DISABLE_EPHEMERAL_APPS = !Build.IS_DEBUGGABLE;
private static final int RADIO_UID = Process.PHONE_UID;
private static final int LOG_UID = Process.LOG_UID;
@@ -462,6 +457,9 @@
private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
+ private static int DEFAULT_EPHEMERAL_HASH_PREFIX_MASK = 0xFFFFF000;
+ private static int DEFAULT_EPHEMERAL_HASH_PREFIX_COUNT = 5;
+
/** Permission grant: not grant the permission. */
private static final int GRANT_DENIED = 1;
@@ -622,9 +620,9 @@
@GuardedBy("mPackages")
final ArraySet<String> mFrozenPackages = new ArraySet<>();
- final ProtectedPackages mProtectedPackages = new ProtectedPackages();
+ final ProtectedPackages mProtectedPackages;
- boolean mRestoredSettings;
+ boolean mFirstBoot;
// System configuration read by SystemConfig.
final int[] mGlobalGids;
@@ -737,8 +735,7 @@
final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
= new SparseArray<IntentFilterVerificationState>();
- final DefaultPermissionGrantPolicy mDefaultPermissionPolicy =
- new DefaultPermissionGrantPolicy(this);
+ final DefaultPermissionGrantPolicy mDefaultPermissionPolicy;
// List of packages names to keep cached, even if they are uninstalled for all users
private List<String> mKeepUninstalledPackages;
@@ -1125,204 +1122,7 @@
final @NonNull String mSharedSystemSharedLibraryPackageName;
private final PackageUsage mPackageUsage = new PackageUsage();
-
- private class PackageUsage {
- private static final int WRITE_INTERVAL
- = (DEBUG_DEXOPT) ? 0 : 30*60*1000; // 30m in ms
-
- private final Object mFileLock = new Object();
- private final AtomicLong mLastWritten = new AtomicLong(0);
- private final AtomicBoolean mBackgroundWriteRunning = new AtomicBoolean(false);
-
- private boolean mIsHistoricalPackageUsageAvailable = true;
-
- boolean isHistoricalPackageUsageAvailable() {
- return mIsHistoricalPackageUsageAvailable;
- }
-
- void write(boolean force) {
- if (force) {
- writeInternal();
- return;
- }
- if (SystemClock.elapsedRealtime() - mLastWritten.get() < WRITE_INTERVAL
- && !DEBUG_DEXOPT) {
- return;
- }
- if (mBackgroundWriteRunning.compareAndSet(false, true)) {
- new Thread("PackageUsage_DiskWriter") {
- @Override
- public void run() {
- try {
- writeInternal();
- } finally {
- mBackgroundWriteRunning.set(false);
- }
- }
- }.start();
- }
- }
-
- private void writeInternal() {
- synchronized (mPackages) {
- synchronized (mFileLock) {
- AtomicFile file = getFile();
- FileOutputStream f = null;
- try {
- f = file.startWrite();
- BufferedOutputStream out = new BufferedOutputStream(f);
- FileUtils.setPermissions(file.getBaseFile().getPath(),
- 0640, SYSTEM_UID, PACKAGE_INFO_GID);
- StringBuilder sb = new StringBuilder();
-
- sb.append(USAGE_FILE_MAGIC_VERSION_1);
- sb.append('\n');
- out.write(sb.toString().getBytes(StandardCharsets.US_ASCII));
-
- for (PackageParser.Package pkg : mPackages.values()) {
- if (pkg.getLatestPackageUseTimeInMills() == 0L) {
- continue;
- }
- sb.setLength(0);
- sb.append(pkg.packageName);
- for (long usageTimeInMillis : pkg.mLastPackageUsageTimeInMills) {
- sb.append(' ');
- sb.append(usageTimeInMillis);
- }
- sb.append('\n');
- out.write(sb.toString().getBytes(StandardCharsets.US_ASCII));
- }
- out.flush();
- file.finishWrite(f);
- } catch (IOException e) {
- if (f != null) {
- file.failWrite(f);
- }
- Log.e(TAG, "Failed to write package usage times", e);
- }
- }
- }
- mLastWritten.set(SystemClock.elapsedRealtime());
- }
-
- void readLP() {
- synchronized (mFileLock) {
- AtomicFile file = getFile();
- BufferedInputStream in = null;
- try {
- in = new BufferedInputStream(file.openRead());
- StringBuffer sb = new StringBuffer();
-
- String firstLine = readLine(in, sb);
- if (firstLine == null) {
- // Empty file. Do nothing.
- } else if (USAGE_FILE_MAGIC_VERSION_1.equals(firstLine)) {
- readVersion1LP(in, sb);
- } else {
- readVersion0LP(in, sb, firstLine);
- }
- } catch (FileNotFoundException expected) {
- mIsHistoricalPackageUsageAvailable = false;
- } catch (IOException e) {
- Log.w(TAG, "Failed to read package usage times", e);
- } finally {
- IoUtils.closeQuietly(in);
- }
- }
- mLastWritten.set(SystemClock.elapsedRealtime());
- }
-
- private void readVersion0LP(InputStream in, StringBuffer sb, String firstLine)
- throws IOException {
- // Initial version of the file had no version number and stored one
- // package-timestamp pair per line.
- // Note that the first line has already been read from the InputStream.
- for (String line = firstLine; line != null; line = readLine(in, sb)) {
- String[] tokens = line.split(" ");
- if (tokens.length != 2) {
- throw new IOException("Failed to parse " + line +
- " as package-timestamp pair.");
- }
-
- String packageName = tokens[0];
- PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg == null) {
- continue;
- }
-
- long timestamp = parseAsLong(tokens[1]);
- for (int reason = 0;
- reason < PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT;
- reason++) {
- pkg.mLastPackageUsageTimeInMills[reason] = timestamp;
- }
- }
- }
-
- private void readVersion1LP(InputStream in, StringBuffer sb) throws IOException {
- // Version 1 of the file started with the corresponding version
- // number and then stored a package name and eight timestamps per line.
- String line;
- while ((line = readLine(in, sb)) != null) {
- String[] tokens = line.split(" ");
- if (tokens.length != PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT + 1) {
- throw new IOException("Failed to parse " + line + " as a timestamp array.");
- }
-
- String packageName = tokens[0];
- PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg == null) {
- continue;
- }
-
- for (int reason = 0;
- reason < PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT;
- reason++) {
- pkg.mLastPackageUsageTimeInMills[reason] = parseAsLong(tokens[reason + 1]);
- }
- }
- }
-
- private long parseAsLong(String token) throws IOException {
- try {
- return Long.parseLong(token);
- } catch (NumberFormatException e) {
- throw new IOException("Failed to parse " + token + " as a long.", e);
- }
- }
-
- private String readLine(InputStream in, StringBuffer sb) throws IOException {
- return readToken(in, sb, '\n');
- }
-
- private String readToken(InputStream in, StringBuffer sb, char endOfToken)
- throws IOException {
- sb.setLength(0);
- while (true) {
- int ch = in.read();
- if (ch == -1) {
- if (sb.length() == 0) {
- return null;
- }
- throw new IOException("Unexpected EOF");
- }
- if (ch == endOfToken) {
- return sb.toString();
- }
- sb.append((char)ch);
- }
- }
-
- private AtomicFile getFile() {
- File dataDir = Environment.getDataDirectory();
- File systemDir = new File(dataDir, "system");
- File fname = new File(systemDir, "package-usage.list");
- return new AtomicFile(fname);
- }
-
- private static final String USAGE_FILE_MAGIC = "PACKAGE_USAGE__VERSION_";
- private static final String USAGE_FILE_MAGIC_VERSION_1 = USAGE_FILE_MAGIC + "1";
- }
+ private final CompilerStats mCompilerStats = new CompilerStats();
class PackageHandler extends Handler {
private boolean mBound = false;
@@ -2160,10 +1960,6 @@
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
m.enableSystemUserPackages();
- // Disable any carrier apps. We do this very early in boot to prevent the apps from being
- // disabled after already being started.
- CarrierAppUtils.disableCarrierAppsUntilPrivileged(context.getOpPackageName(), m,
- UserHandle.USER_SYSTEM);
ServiceManager.addService("package", m);
return m;
}
@@ -2217,6 +2013,34 @@
displayManager.getDisplay(Display.DEFAULT_DISPLAY).getMetrics(metrics);
}
+ /**
+ * Requests that files preopted on a secondary system partition be copied to the data partition
+ * if possible. Note that the actual copying of the files is accomplished by init for security
+ * reasons. This simply requests that the copy takes place and awaits confirmation of its
+ * completion. See platform/system/extras/cppreopt/ for the implementation of the actual copy.
+ */
+ private static void requestCopyPreoptedFiles() {
+ final int WAIT_TIME_MS = 100;
+ final String CP_PREOPT_PROPERTY = "sys.cppreopt";
+ if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) {
+ SystemProperties.set(CP_PREOPT_PROPERTY, "requested");
+ // We will wait for up to 100 seconds.
+ final long timeEnd = SystemClock.uptimeMillis() + 100 * 1000;
+ while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) {
+ try {
+ Thread.sleep(WAIT_TIME_MS);
+ } catch (InterruptedException e) {
+ // Do nothing
+ }
+ if (SystemClock.uptimeMillis() > timeEnd) {
+ SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out");
+ Slog.wtf(TAG, "cppreopt did not finish!");
+ break;
+ }
+ }
+ }
+ }
+
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
@@ -2276,6 +2100,8 @@
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();
+ mProtectedPackages = new ProtectedPackages(mContext);
+
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
@@ -2286,6 +2112,8 @@
mProcessLoggingHandler = new ProcessLoggingHandler();
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
+ mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(this);
+
File dataDir = Environment.getDataDirectory();
mAppInstallDir = new File(dataDir, "app");
mAppLib32InstallDir = new File(dataDir, "app-lib");
@@ -2318,7 +2146,11 @@
mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
- mRestoredSettings = mSettings.readLPw(sUserManager.getUsers(false));
+ mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
+
+ if (mFirstBoot) {
+ requestCopyPreoptedFiles();
+ }
String customResolverActivity = Resources.getSystem().getString(
R.string.config_customResolverActivity);
@@ -2669,7 +2501,8 @@
// Now that we know all the packages we are keeping,
// read and update their last usage times.
- mPackageUsage.readLP();
+ mPackageUsage.read(mPackages);
+ mCompilerStats.read();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
SystemClock.uptimeMillis());
@@ -2695,7 +2528,7 @@
// If this is the first boot or an update from pre-M, and it is a normal
// boot, then we need to initialize the default preferred apps across
// all defined users.
- if (!onlyCore && (mPromoteSystemApps || !mRestoredSettings)) {
+ if (!onlyCore && (mPromoteSystemApps || mFirstBoot)) {
for (UserInfo user : sUserManager.getUsers(true)) {
mSettings.applyDefaultPreferredAppsLPw(this, user.id);
applyFactoryDefaultBrowserLPw(user.id);
@@ -2761,7 +2594,7 @@
}
}
- int[] stats = performDexOpt(coreApps, false,
+ int[] stats = performDexOptUpgrade(coreApps, false,
getCompilerFilterForReason(REASON_CORE_APP));
final int elapsedTimeSeconds =
@@ -2853,7 +2686,7 @@
@Override
public boolean isFirstBoot() {
- return !mRestoredSettings;
+ return mFirstBoot;
}
@Override
@@ -2940,17 +2773,20 @@
private @Nullable ComponentName getEphemeralResolverLPr() {
final String[] packageArray =
mContext.getResources().getStringArray(R.array.config_ephemeralResolverPackage);
- if (packageArray.length == 0) {
+ if (packageArray.length == 0 && !Build.IS_DEBUGGABLE) {
if (DEBUG_EPHEMERAL) {
Slog.d(TAG, "Ephemeral resolver NOT found; empty package list");
}
return null;
}
+ final int resolveFlags =
+ MATCH_DIRECT_BOOT_AWARE
+ | MATCH_DIRECT_BOOT_UNAWARE
+ | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0);
final Intent resolverIntent = new Intent(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE);
final List<ResolveInfo> resolvers = queryIntentServicesInternal(resolverIntent, null,
- MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
- UserHandle.USER_SYSTEM);
+ resolveFlags, UserHandle.USER_SYSTEM);
final int N = resolvers.size();
if (N == 0) {
@@ -2969,7 +2805,7 @@
}
final String packageName = info.serviceInfo.packageName;
- if (!possiblePackages.contains(packageName)) {
+ if (!possiblePackages.contains(packageName) && !Build.IS_DEBUGGABLE) {
if (DEBUG_EPHEMERAL) {
Slog.d(TAG, "Ephemeral resolver not in allowed package list;"
+ " pkg: " + packageName + ", info:" + info);
@@ -2994,9 +2830,12 @@
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
+ final int resolveFlags =
+ MATCH_DIRECT_BOOT_AWARE
+ | MATCH_DIRECT_BOOT_UNAWARE
+ | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0);
final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
- MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
- UserHandle.USER_SYSTEM);
+ resolveFlags, UserHandle.USER_SYSTEM);
if (matches.size() == 0) {
return null;
} else if (matches.size() == 1) {
@@ -3171,8 +3010,12 @@
final PermissionsState permissionsState = ps.getPermissionsState();
- final int[] gids = permissionsState.computeGids(userId);
- final Set<String> permissions = permissionsState.getPermissions(userId);
+ // Compute GIDs only if requested
+ final int[] gids = (flags & PackageManager.GET_GIDS) == 0
+ ? EMPTY_INT_ARRAY : permissionsState.computeGids(userId);
+ // Compute granted permissions only if package has requested permissions
+ final Set<String> permissions = ArrayUtils.isEmpty(p.requestedPermissions)
+ ? Collections.<String>emptySet() : permissionsState.getPermissions(userId);
final PackageUserState state = ps.readUserState(userId);
return PackageParser.generatePackageInfo(p, gids, flags,
@@ -4985,48 +4828,45 @@
private EphemeralResolveInfo getEphemeralResolveInfo(Intent intent, String resolvedType,
int userId) {
- MessageDigest digest = null;
- try {
- digest = MessageDigest.getInstance(EphemeralResolveInfo.SHA_ALGORITHM);
- } catch (NoSuchAlgorithmException e) {
- // If we can't create a digest, ignore ephemeral apps.
- return null;
- }
-
- final byte[] hostBytes = intent.getData().getHost().getBytes();
- final byte[] digestBytes = digest.digest(hostBytes);
- int shaPrefix =
- digestBytes[0] << 24
- | digestBytes[1] << 16
- | digestBytes[2] << 8
- | digestBytes[3] << 0;
+ final int ephemeralPrefixMask = Global.getInt(mContext.getContentResolver(),
+ Global.EPHEMERAL_HASH_PREFIX_MASK, DEFAULT_EPHEMERAL_HASH_PREFIX_MASK);
+ final int ephemeralPrefixCount = Global.getInt(mContext.getContentResolver(),
+ Global.EPHEMERAL_HASH_PREFIX_COUNT, DEFAULT_EPHEMERAL_HASH_PREFIX_COUNT);
+ final EphemeralDigest digest = new EphemeralDigest(intent.getData(), ephemeralPrefixMask,
+ ephemeralPrefixCount);
+ final int[] shaPrefix = digest.getDigestPrefix();
+ final byte[][] digestBytes = digest.getDigestBytes();
final List<EphemeralResolveInfo> ephemeralResolveInfoList =
- mEphemeralResolverConnection.getEphemeralResolveInfoList(shaPrefix);
+ mEphemeralResolverConnection.getEphemeralResolveInfoList(
+ shaPrefix, ephemeralPrefixMask);
if (ephemeralResolveInfoList == null || ephemeralResolveInfoList.size() == 0) {
// No hash prefix match; there are no ephemeral apps for this domain.
return null;
}
- for (int i = ephemeralResolveInfoList.size() - 1; i >= 0; --i) {
- EphemeralResolveInfo ephemeralApplication = ephemeralResolveInfoList.get(i);
- if (!Arrays.equals(digestBytes, ephemeralApplication.getDigestBytes())) {
- continue;
- }
- final List<IntentFilter> filters = ephemeralApplication.getFilters();
- // No filters; this should never happen.
- if (filters.isEmpty()) {
- continue;
- }
- // We have a domain match; resolve the filters to see if anything matches.
- final EphemeralIntentResolver ephemeralResolver = new EphemeralIntentResolver();
- for (int j = filters.size() - 1; j >= 0; --j) {
- final EphemeralResolveIntentInfo intentInfo =
- new EphemeralResolveIntentInfo(filters.get(j), ephemeralApplication);
- ephemeralResolver.addFilter(intentInfo);
- }
- List<EphemeralResolveInfo> matchedResolveInfoList = ephemeralResolver.queryIntent(
- intent, resolvedType, false /*defaultOnly*/, userId);
- if (!matchedResolveInfoList.isEmpty()) {
- return matchedResolveInfoList.get(0);
+
+ // Go in reverse order so we match the narrowest scope first.
+ for (int i = shaPrefix.length - 1; i >= 0 ; --i) {
+ for (EphemeralResolveInfo ephemeralApplication : ephemeralResolveInfoList) {
+ if (!Arrays.equals(digestBytes[i], ephemeralApplication.getDigestBytes())) {
+ continue;
+ }
+ final List<IntentFilter> filters = ephemeralApplication.getFilters();
+ // No filters; this should never happen.
+ if (filters.isEmpty()) {
+ continue;
+ }
+ // We have a domain match; resolve the filters to see if anything matches.
+ final EphemeralIntentResolver ephemeralResolver = new EphemeralIntentResolver();
+ for (int j = filters.size() - 1; j >= 0; --j) {
+ final EphemeralResolveIntentInfo intentInfo =
+ new EphemeralResolveIntentInfo(filters.get(j), ephemeralApplication);
+ ephemeralResolver.addFilter(intentInfo);
+ }
+ List<EphemeralResolveInfo> matchedResolveInfoList = ephemeralResolver.queryIntent(
+ intent, resolvedType, false /*defaultOnly*/, userId);
+ if (!matchedResolveInfoList.isEmpty()) {
+ return matchedResolveInfoList.get(0);
+ }
}
}
// Hash or filter mis-match; no ephemeral apps for this domain.
@@ -6755,11 +6595,26 @@
}
}
+ private long getLastModifiedTime(PackageParser.Package pkg, File srcFile) {
+ if (srcFile.isDirectory()) {
+ final File baseFile = new File(pkg.baseCodePath);
+ long maxModifiedTime = baseFile.lastModified();
+ if (pkg.splitCodePaths != null) {
+ for (int i = pkg.splitCodePaths.length - 1; i >=0; --i) {
+ final File splitFile = new File(pkg.splitCodePaths[i]);
+ maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified());
+ }
+ }
+ return maxModifiedTime;
+ }
+ return srcFile.lastModified();
+ }
+
private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg, File srcFile,
final int policyFlags) throws PackageManagerException {
if (ps != null
&& ps.codePath.equals(srcFile)
- && ps.timeStamp == srcFile.lastModified()
+ && ps.timeStamp == getLastModifiedTime(pkg, srcFile)
&& !isCompatSignatureUpdateNeeded(pkg)
&& !isRecoverSignatureUpdateNeeded(pkg)) {
long mSigningKeySetId = ps.keySetData.getProperSigningKeySet();
@@ -7206,22 +7061,17 @@
try {
IMountService ms = PackageHelper.getMountService();
if (ms != null) {
- final boolean isUpgrade = isUpgrade();
- boolean doTrim = isUpgrade;
- if (doTrim) {
- Slog.w(TAG, "Running disk maintenance immediately due to system update");
- } else {
- final long interval = android.provider.Settings.Global.getLong(
- mContext.getContentResolver(),
- android.provider.Settings.Global.FSTRIM_MANDATORY_INTERVAL,
- DEFAULT_MANDATORY_FSTRIM_INTERVAL);
- if (interval > 0) {
- final long timeSinceLast = System.currentTimeMillis() - ms.lastMaintenance();
- if (timeSinceLast > interval) {
- doTrim = true;
- Slog.w(TAG, "No disk maintenance in " + timeSinceLast
- + "; running immediately");
- }
+ boolean doTrim = false;
+ final long interval = android.provider.Settings.Global.getLong(
+ mContext.getContentResolver(),
+ android.provider.Settings.Global.FSTRIM_MANDATORY_INTERVAL,
+ DEFAULT_MANDATORY_FSTRIM_INTERVAL);
+ if (interval > 0) {
+ final long timeSinceLast = System.currentTimeMillis() - ms.lastMaintenance();
+ if (timeSinceLast > interval) {
+ doTrim = true;
+ Slog.w(TAG, "No disk maintenance in " + timeSinceLast
+ + "; running immediately");
}
}
if (doTrim) {
@@ -7268,7 +7118,7 @@
}
final long startTime = System.nanoTime();
- final int[] stats = performDexOpt(pkgs, mIsPreNUpgrade /* showDialog */,
+ final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */,
getCompilerFilterForReason(causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT));
final int elapsedTimeSeconds =
@@ -7287,7 +7137,7 @@
* which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped}
* and {@code numberOfPackagesFailed}.
*/
- private int[] performDexOpt(List<PackageParser.Package> pkgs, boolean showDialog,
+ private int[] performDexOptUpgrade(List<PackageParser.Package> pkgs, boolean showDialog,
String compilerFilter) {
int numberOfPackagesVisited = 0;
@@ -7321,6 +7171,19 @@
}
}
+ // If the OTA updates a system app which was previously preopted to a non-preopted state
+ // the app might end up being verified at runtime. That's because by default the apps
+ // are verify-profile but for preopted apps there's no profile.
+ // Do a hacky check to ensure that if we have no profiles (a reasonable indication
+ // that before the OTA the app was preopted) the app gets compiled with a non-profile
+ // filter (by default interpret-only).
+ // Note that at this stage unused apps are already filtered.
+ if (isSystemApp(pkg) &&
+ DexFile.isProfileGuidedCompilerFilter(compilerFilter) &&
+ !Environment.getReferenceProfile(pkg.packageName).exists()) {
+ compilerFilter = getNonProfileGuidedCompilerFilter(compilerFilter);
+ }
+
// checkProfiles is false to avoid merging profiles during boot which
// might interfere with background compilation (b/28612421).
// Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will
@@ -7407,7 +7270,8 @@
// Package could not be found. Report failure.
return PackageDexOptimizer.DEX_OPT_FAILED;
}
- mPackageUsage.write(false);
+ mPackageUsage.maybeWriteAsync(mPackages);
+ mCompilerStats.maybeWriteAsync();
}
long callingId = Binder.clearCallingIdentity();
try {
@@ -7452,11 +7316,12 @@
// Currently this will do a full compilation of the library by default.
pdo.performDexOpt(depPackage, null /* sharedLibraries */, instructionSets,
false /* checkProfiles */,
- getCompilerFilterForReason(REASON_NON_SYSTEM_LIBRARY));
+ getCompilerFilterForReason(REASON_NON_SYSTEM_LIBRARY),
+ getOrCreateCompilerPackageStats(depPackage));
}
}
return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets, checkProfiles,
- targetCompilerFilter);
+ targetCompilerFilter, getOrCreateCompilerPackageStats(p));
}
Collection<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package p) {
@@ -7510,7 +7375,8 @@
}
public void shutdown() {
- mPackageUsage.write(true);
+ mPackageUsage.writeNow(mPackages);
+ mCompilerStats.writeNow();
}
@Override
@@ -7961,7 +7827,9 @@
} catch (IOException ignore) {
} finally {
try {
- jarFile.close();
+ if (jarFile != null) {
+ jarFile.close();
+ }
} catch (IOException ignore) {}
}
return false;
@@ -8395,7 +8263,7 @@
final String pkgName = pkg.packageName;
- final long scanFileTime = scanFile.lastModified();
+ final long scanFileTime = getLastModifiedTime(pkg, scanFile);
final boolean forceDex = (scanFlags & SCAN_FORCE_DEX) != 0;
pkg.applicationInfo.processName = fixProcessName(
pkg.applicationInfo.packageName,
@@ -11638,6 +11506,18 @@
if (pkgSetting == null) {
return false;
}
+ // Do not allow "android" is being disabled
+ if ("android".equals(packageName)) {
+ Slog.w(TAG, "Cannot hide package: android");
+ return false;
+ }
+ // Only allow protected packages to hide themselves.
+ if (hidden && !UserHandle.isSameApp(uid, pkgSetting.appId)
+ && mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+ Slog.w(TAG, "Not hiding protected package: " + packageName);
+ return false;
+ }
+
if (pkgSetting.getHidden(userId) != hidden) {
pkgSetting.setHidden(hidden, userId);
mSettings.writePackageRestrictionsLPr(userId);
@@ -11878,6 +11758,12 @@
return false;
}
+ if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+ Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+ + "\": protected package");
+ return false;
+ }
+
return true;
}
@@ -12411,6 +12297,9 @@
public void run() {
for (int i = 0; i < mRunningInstalls.size(); i++) {
final PostInstallData data = mRunningInstalls.valueAt(i);
+ if (data.res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
+ continue;
+ }
if (pkgName.equals(data.res.pkg.applicationInfo.packageName)) {
// right package; but is it for the right user?
for (int uIndex = 0; uIndex < data.res.newUsers.length; uIndex++) {
@@ -15142,7 +15031,8 @@
// Also, don't fail application installs if the dexopt step fails.
mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
null /* instructionSets */, false /* checkProfiles */,
- getCompilerFilterForReason(REASON_INSTALL));
+ getCompilerFilterForReason(REASON_INSTALL),
+ getOrCreateCompilerPackageStats(pkg));
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
// Notify BackgroundDexOptService that the package has been changed.
@@ -15933,6 +15823,12 @@
+ e.getMessage());
return false;
}
+ try {
+ // update shared libraries for the newly re-installed system package
+ updateSharedLibrariesLPw(newPkg, null);
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
+ }
prepareAppDataAfterInstallLIF(newPkg);
@@ -16408,8 +16304,9 @@
enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */, "clear application data");
- if (mProtectedPackages.canPackageBeWiped(userId, packageName)) {
- throw new SecurityException("Cannot clear data for a device owner or a profile owner");
+ if (mProtectedPackages.isPackageDataProtected(userId, packageName)) {
+ throw new SecurityException("Cannot clear data for a protected package: "
+ + packageName);
}
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(new Runnable() {
@@ -16801,9 +16698,28 @@
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
scheduleWritePackageRestrictionsLocked(userId);
+ postPreferredActivityChangedBroadcast(userId);
}
}
+ private void postPreferredActivityChangedBroadcast(int userId) {
+ mHandler.post(() -> {
+ final IActivityManager am = ActivityManagerNative.getDefault();
+ if (am == null) {
+ return;
+ }
+
+ final Intent intent = new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ try {
+ am.broadcastIntent(null, intent, null, null,
+ 0, null, null, null, android.app.AppOpsManager.OP_NONE,
+ null, false, false, userId);
+ } catch (RemoteException e) {
+ }
+ });
+ }
+
@Override
public void replacePreferredActivity(IntentFilter filter, int match,
ComponentName[] set, ComponentName activity, int userId) {
@@ -16955,6 +16871,9 @@
changed = true;
}
}
+ if (changed) {
+ postPreferredActivityChangedBroadcast(userId);
+ }
return changed;
}
@@ -17068,6 +16987,7 @@
mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter(
new PersistentPreferredActivity(filter, activity));
scheduleWritePackageRestrictionsLocked(userId);
+ postPreferredActivityChangedBroadcast(userId);
}
}
@@ -17110,6 +17030,7 @@
if (changed) {
scheduleWritePackageRestrictionsLocked(userId);
+ postPreferredActivityChangedBroadcast(userId);
}
}
}
@@ -17595,6 +17516,7 @@
private Intent getHomeIntent() {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
return intent;
}
@@ -17735,9 +17657,9 @@
+ Binder.getCallingPid()
+ ", uid=" + uid + ", package uid=" + pkgSetting.appId);
}
- // Don't allow changing profile and device owners.
- if (mProtectedPackages.canPackageStateBeChanged(userId, packageName)) {
- throw new SecurityException("Cannot disable a device owner or a profile owner");
+ // Don't allow changing protected packages.
+ if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+ throw new SecurityException("Cannot disable a protected package: " + packageName);
}
}
@@ -17954,6 +17876,11 @@
public void systemReady() {
mSystemReady = true;
+ // Disable any carrier apps. We do this very early in boot to prevent the apps from being
+ // disabled after already being started.
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), this,
+ mContext.getContentResolver(), UserHandle.USER_SYSTEM);
+
// Read the compatibilty setting when the system is ready.
boolean compatibilityModeEnabled = android.provider.Settings.Global.getInt(
mContext.getContentResolver(),
@@ -18009,6 +17936,13 @@
mDefaultPermissionPolicy.grantDefaultPermissions(userId);
}
+ // If we did not grant default permissions, we preload from this the
+ // default permission exceptions lazily to ensure we don't hit the
+ // disk on a new user creation.
+ if (grantPermissionsUserIds == EMPTY_INT_ARRAY) {
+ mDefaultPermissionPolicy.scheduleReadDefaultPermissionExceptions();
+ }
+
// Kick off any messages waiting for system ready
if (mPostSystemReadyMessages != null) {
for (Message msg : mPostSystemReadyMessages) {
@@ -18101,6 +18035,7 @@
public static final int DUMP_DOMAIN_PREFERRED = 1 << 18;
public static final int DUMP_FROZEN = 1 << 19;
public static final int DUMP_DEXOPT = 1 << 20;
+ public static final int DUMP_COMPILER_STATS = 1 << 21;
public static final int OPTION_SHOW_FILTERS = 1 << 0;
@@ -18218,6 +18153,7 @@
pw.println(" installs: details about install sessions");
pw.println(" check-permission <permission> <package> [<user>]: does pkg hold perm?");
pw.println(" dexopt: dump dexopt state");
+ pw.println(" compiler-stats: dump compiler statistics");
pw.println(" <package.name>: info about given package");
return;
} else if ("--checkin".equals(opt)) {
@@ -18339,6 +18275,8 @@
dumpState.setDump(DumpState.DUMP_FROZEN);
} else if ("dexopt".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_DEXOPT);
+ } else if ("compiler-stats".equals(cmd)) {
+ dumpState.setDump(DumpState.DUMP_COMPILER_STATS);
} else if ("write".equals(cmd)) {
synchronized (mPackages) {
mSettings.writeLPr();
@@ -18701,6 +18639,11 @@
dumpDexoptStateLPr(pw, packageName);
}
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) {
+ if (dumpState.onTitlePrinted()) pw.println();
+ dumpCompilerStatsLPr(pw, packageName);
+ }
+
if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
if (dumpState.onTitlePrinted()) pw.println();
mSettings.dumpReadMessagesLPr(pw, dumpState);
@@ -18765,6 +18708,38 @@
}
}
+ private void dumpCompilerStatsLPr(PrintWriter pw, String packageName) {
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
+ ipw.println();
+ ipw.println("Compiler stats:");
+ ipw.increaseIndent();
+ Collection<PackageParser.Package> packages = null;
+ if (packageName != null) {
+ PackageParser.Package targetPackage = mPackages.get(packageName);
+ if (targetPackage != null) {
+ packages = Collections.singletonList(targetPackage);
+ } else {
+ ipw.println("Unable to find package: " + packageName);
+ return;
+ }
+ } else {
+ packages = mPackages.values();
+ }
+
+ for (PackageParser.Package pkg : packages) {
+ ipw.println("[" + pkg.packageName + "]");
+ ipw.increaseIndent();
+
+ CompilerStats.PackageStats stats = getCompilerPackageStats(pkg.packageName);
+ if (stats == null) {
+ ipw.println("(No recorded stats)");
+ } else {
+ stats.dump(ipw);
+ }
+ ipw.decreaseIndent();
+ }
+ }
+
private String dumpDomainString(String packageName) {
List<IntentFilterVerificationInfo> iviList = getIntentFilterVerifications(packageName)
.getList();
@@ -20363,12 +20338,7 @@
}
}
- void onBeforeUserStartUninitialized(final int userId) {
- synchronized (mPackages) {
- if (mSettings.areDefaultRuntimePermissionsGrantedLPr(userId)) {
- return;
- }
- }
+ void onNewUserCreated(final int userId) {
mDefaultPermissionPolicy.grantDefaultPermissions(userId);
// If permission review for legacy apps is required, we represent
// dagerous permissions for such apps as always granted runtime
@@ -20869,9 +20839,8 @@
}
@Override
- public boolean canPackageBeWiped(int userId, String packageName) {
- return mProtectedPackages.canPackageBeWiped(userId,
- packageName);
+ public boolean isPackageDataProtected(int userId, String packageName) {
+ return mProtectedPackages.isPackageDataProtected(userId, packageName);
}
}
@@ -20932,4 +20901,20 @@
msg.setData(data);
mProcessLoggingHandler.sendMessage(msg);
}
+
+ public CompilerStats.PackageStats getCompilerPackageStats(String pkgName) {
+ return mCompilerStats.getPackageStats(pkgName);
+ }
+
+ public CompilerStats.PackageStats getOrCreateCompilerPackageStats(PackageParser.Package pkg) {
+ return getOrCreateCompilerPackageStats(pkg.packageName);
+ }
+
+ public CompilerStats.PackageStats getOrCreateCompilerPackageStats(String pkgName) {
+ return mCompilerStats.getOrCreatePackageStats(pkgName);
+ }
+
+ public void deleteCompilerPackageStats(String pkgName) {
+ mCompilerStats.deletePackageStats(pkgName);
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 07dc404..e5ddfd0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1099,8 +1099,9 @@
try {
mInterface.setHomeActivity(componentName, userId);
+ pw.println("Success");
return 0;
- } catch (RemoteException e) {
+ } catch (Exception e) {
pw.println(e.toString());
return 1;
}
@@ -1235,7 +1236,7 @@
PackageInstaller.STATUS_FAILURE);
if (status == PackageInstaller.STATUS_SUCCESS) {
if (logSuccess) {
- System.out.println("Success");
+ pw.println("Success");
}
} else {
pw.println("Failure ["
diff --git a/services/core/java/com/android/server/pm/PackageUsage.java b/services/core/java/com/android/server/pm/PackageUsage.java
new file mode 100644
index 0000000..ac1f739
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageUsage.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.os.Process.PACKAGE_INFO_GID;
+import static android.os.Process.SYSTEM_UID;
+
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.os.FileUtils;
+import android.util.AtomicFile;
+import android.util.Log;
+
+import libcore.io.IoUtils;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+
+class PackageUsage extends AbstractStatsBase<Map<String, PackageParser.Package>> {
+
+ private static final String USAGE_FILE_MAGIC = "PACKAGE_USAGE__VERSION_";
+ private static final String USAGE_FILE_MAGIC_VERSION_1 = USAGE_FILE_MAGIC + "1";
+
+ private boolean mIsHistoricalPackageUsageAvailable = true;
+
+ PackageUsage() {
+ super("package-usage.list", "PackageUsage_DiskWriter", /* lock */ true);
+ }
+
+ boolean isHistoricalPackageUsageAvailable() {
+ return mIsHistoricalPackageUsageAvailable;
+ }
+
+ @Override
+ protected void writeInternal(Map<String, PackageParser.Package> packages) {
+ AtomicFile file = getFile();
+ FileOutputStream f = null;
+ try {
+ f = file.startWrite();
+ BufferedOutputStream out = new BufferedOutputStream(f);
+ FileUtils.setPermissions(file.getBaseFile().getPath(),
+ 0640, SYSTEM_UID, PACKAGE_INFO_GID);
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(USAGE_FILE_MAGIC_VERSION_1);
+ sb.append('\n');
+ out.write(sb.toString().getBytes(StandardCharsets.US_ASCII));
+
+ for (PackageParser.Package pkg : packages.values()) {
+ if (pkg.getLatestPackageUseTimeInMills() == 0L) {
+ continue;
+ }
+ sb.setLength(0);
+ sb.append(pkg.packageName);
+ for (long usageTimeInMillis : pkg.mLastPackageUsageTimeInMills) {
+ sb.append(' ');
+ sb.append(usageTimeInMillis);
+ }
+ sb.append('\n');
+ out.write(sb.toString().getBytes(StandardCharsets.US_ASCII));
+ }
+ out.flush();
+ file.finishWrite(f);
+ } catch (IOException e) {
+ if (f != null) {
+ file.failWrite(f);
+ }
+ Log.e(PackageManagerService.TAG, "Failed to write package usage times", e);
+ }
+ }
+
+ @Override
+ protected void readInternal(Map<String, PackageParser.Package> packages) {
+ AtomicFile file = getFile();
+ BufferedInputStream in = null;
+ try {
+ in = new BufferedInputStream(file.openRead());
+ StringBuffer sb = new StringBuffer();
+
+ String firstLine = readLine(in, sb);
+ if (firstLine == null) {
+ // Empty file. Do nothing.
+ } else if (USAGE_FILE_MAGIC_VERSION_1.equals(firstLine)) {
+ readVersion1LP(packages, in, sb);
+ } else {
+ readVersion0LP(packages, in, sb, firstLine);
+ }
+ } catch (FileNotFoundException expected) {
+ mIsHistoricalPackageUsageAvailable = false;
+ } catch (IOException e) {
+ Log.w(PackageManagerService.TAG, "Failed to read package usage times", e);
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ }
+
+ private void readVersion0LP(Map<String, PackageParser.Package> packages, InputStream in,
+ StringBuffer sb, String firstLine)
+ throws IOException {
+ // Initial version of the file had no version number and stored one
+ // package-timestamp pair per line.
+ // Note that the first line has already been read from the InputStream.
+ for (String line = firstLine; line != null; line = readLine(in, sb)) {
+ String[] tokens = line.split(" ");
+ if (tokens.length != 2) {
+ throw new IOException("Failed to parse " + line +
+ " as package-timestamp pair.");
+ }
+
+ String packageName = tokens[0];
+ PackageParser.Package pkg = packages.get(packageName);
+ if (pkg == null) {
+ continue;
+ }
+
+ long timestamp = parseAsLong(tokens[1]);
+ for (int reason = 0;
+ reason < PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT;
+ reason++) {
+ pkg.mLastPackageUsageTimeInMills[reason] = timestamp;
+ }
+ }
+ }
+
+ private void readVersion1LP(Map<String, PackageParser.Package> packages, InputStream in,
+ StringBuffer sb) throws IOException {
+ // Version 1 of the file started with the corresponding version
+ // number and then stored a package name and eight timestamps per line.
+ String line;
+ while ((line = readLine(in, sb)) != null) {
+ String[] tokens = line.split(" ");
+ if (tokens.length != PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT + 1) {
+ throw new IOException("Failed to parse " + line + " as a timestamp array.");
+ }
+
+ String packageName = tokens[0];
+ PackageParser.Package pkg = packages.get(packageName);
+ if (pkg == null) {
+ continue;
+ }
+
+ for (int reason = 0;
+ reason < PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT;
+ reason++) {
+ pkg.mLastPackageUsageTimeInMills[reason] = parseAsLong(tokens[reason + 1]);
+ }
+ }
+ }
+
+ private long parseAsLong(String token) throws IOException {
+ try {
+ return Long.parseLong(token);
+ } catch (NumberFormatException e) {
+ throw new IOException("Failed to parse " + token + " as a long.", e);
+ }
+ }
+
+ private String readLine(InputStream in, StringBuffer sb) throws IOException {
+ return readToken(in, sb, '\n');
+ }
+
+ private String readToken(InputStream in, StringBuffer sb, char endOfToken)
+ throws IOException {
+ sb.setLength(0);
+ while (true) {
+ int ch = in.read();
+ if (ch == -1) {
+ if (sb.length() == 0) {
+ return null;
+ }
+ throw new IOException("Unexpected EOF");
+ }
+ if (ch == endOfToken) {
+ return sb.toString();
+ }
+ sb.append((char)ch);
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/PermissionsState.java b/services/core/java/com/android/server/pm/PermissionsState.java
index 007b738..8f9968ec 100644
--- a/services/core/java/com/android/server/pm/PermissionsState.java
+++ b/services/core/java/com/android/server/pm/PermissionsState.java
@@ -274,7 +274,7 @@
return Collections.emptySet();
}
- Set<String> permissions = new ArraySet<>();
+ Set<String> permissions = new ArraySet<>(mPermissions.size());
final int permissionCount = mPermissions.size();
for (int i = 0; i < permissionCount; i++) {
@@ -282,6 +282,7 @@
if (hasInstallPermission(permission)) {
permissions.add(permission);
+ continue;
}
if (userId != UserHandle.USER_ALL) {
diff --git a/services/core/java/com/android/server/pm/ProtectedPackages.java b/services/core/java/com/android/server/pm/ProtectedPackages.java
index 7bdea18..e67364a 100644
--- a/services/core/java/com/android/server/pm/ProtectedPackages.java
+++ b/services/core/java/com/android/server/pm/ProtectedPackages.java
@@ -16,10 +16,15 @@
package com.android.server.pm;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.content.Context;
import android.os.UserHandle;
import android.util.SparseArray;
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+
/**
* Manages package names that need special protection.
*
@@ -29,61 +34,87 @@
*/
public class ProtectedPackages {
@UserIdInt
+ @GuardedBy("this")
private int mDeviceOwnerUserId;
+ @Nullable
+ @GuardedBy("this")
private String mDeviceOwnerPackage;
+ @Nullable
+ @GuardedBy("this")
private SparseArray<String> mProfileOwnerPackages;
- private final Object mLock = new Object();
+ @Nullable
+ @GuardedBy("this")
+ private final String mDeviceProvisioningPackage;
+
+ private final Context mContext;
+
+ public ProtectedPackages(Context context) {
+ mContext = context;
+ mDeviceProvisioningPackage = mContext.getResources().getString(
+ R.string.config_deviceProvisioningPackage);
+ }
/**
* Sets the device/profile owner information.
*/
- public void setDeviceAndProfileOwnerPackages(
+ public synchronized void setDeviceAndProfileOwnerPackages(
int deviceOwnerUserId, String deviceOwnerPackage,
SparseArray<String> profileOwnerPackages) {
- synchronized (mLock) {
- mDeviceOwnerUserId = deviceOwnerUserId;
- mDeviceOwnerPackage =
- (deviceOwnerUserId == UserHandle.USER_NULL) ? null : deviceOwnerPackage;
- mProfileOwnerPackages = (profileOwnerPackages == null) ? null
- : profileOwnerPackages.clone();
- }
+ mDeviceOwnerUserId = deviceOwnerUserId;
+ mDeviceOwnerPackage =
+ (deviceOwnerUserId == UserHandle.USER_NULL) ? null : deviceOwnerPackage;
+ mProfileOwnerPackages = (profileOwnerPackages == null) ? null
+ : profileOwnerPackages.clone();
}
- private boolean hasDeviceOwnerOrProfileOwner(int userId, String packageName) {
+ private synchronized boolean hasDeviceOwnerOrProfileOwner(int userId, String packageName) {
if (packageName == null) {
return false;
}
- synchronized (mLock) {
- if (mDeviceOwnerPackage != null) {
- if ((mDeviceOwnerUserId == userId)
- && (packageName.equals(mDeviceOwnerPackage))) {
- return true;
- }
+ if (mDeviceOwnerPackage != null) {
+ if ((mDeviceOwnerUserId == userId)
+ && (packageName.equals(mDeviceOwnerPackage))) {
+ return true;
}
- if (mProfileOwnerPackages != null) {
- if (packageName.equals(mProfileOwnerPackages.get(userId))) {
- return true;
- }
+ }
+ if (mProfileOwnerPackages != null) {
+ if (packageName.equals(mProfileOwnerPackages.get(userId))) {
+ return true;
}
}
return false;
}
/**
- * Whether a package or the components in a package's enabled state can be changed
- * by other callers than itself.
+ * Returns {@code true} if a given package is protected. Otherwise, returns {@code false}.
+ *
+ * <p>A protected package means that, apart from the package owner, no system or privileged apps
+ * can modify its data or package state.
*/
- public boolean canPackageStateBeChanged(@UserIdInt int userId, String packageName) {
- return hasDeviceOwnerOrProfileOwner(userId, packageName);
+ private synchronized boolean isProtectedPackage(String packageName) {
+ return packageName != null && packageName.equals(mDeviceProvisioningPackage);
}
/**
- * Whether a package's data be cleared.
+ * Returns {@code true} if a given package's state is protected. Otherwise, returns
+ * {@code false}.
+ *
+ * <p>This is not applicable if the caller is the package owner.
*/
- public boolean canPackageBeWiped(@UserIdInt int userId, String packageName) {
- return hasDeviceOwnerOrProfileOwner(userId, packageName);
+ public boolean isPackageStateProtected(@UserIdInt int userId, String packageName) {
+ return hasDeviceOwnerOrProfileOwner(userId, packageName)
+ || isProtectedPackage(packageName);
+ }
+
+ /**
+ * Returns {@code true} if a given package's data is protected. Otherwise, returns
+ * {@code false}.
+ */
+ public boolean isPackageDataProtected(@UserIdInt int userId, String packageName) {
+ return hasDeviceOwnerOrProfileOwner(userId, packageName)
+ || isProtectedPackage(packageName);
}
}
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index a6350fe..8970556 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -19,6 +19,7 @@
import android.content.pm.PackageParser;
import android.content.pm.Signature;
import android.os.Environment;
+import android.os.SystemProperties;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -64,6 +65,8 @@
// to synchronize access during policy load and access attempts.
private static List<Policy> sPolicies = new ArrayList<>();
+ private static final String PROP_FORCE_RESTORECON = "sys.force_restorecon";
+
/** Path to version on rootfs */
private static final File VERSION_FILE = new File("/selinux_version");
@@ -322,6 +325,11 @@
* @return Returns true if the restorecon should occur or false otherwise.
*/
public static boolean isRestoreconNeeded(File file) {
+ // To investigate boot timing, allow a property to always force restorecon
+ if (SystemProperties.getBoolean(PROP_FORCE_RESTORECON, false)) {
+ return true;
+ }
+
try {
final byte[] buf = new byte[20];
final int len = Os.getxattr(file.getAbsolutePath(), XATTR_SEAPP_HASH, buf);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index dfd6dfe..5126305 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4011,7 +4011,7 @@
file.delete();
removeCrossProfileIntentFiltersLPw(userId);
- mRuntimePermissionsPersistence.onUserRemoved(userId);
+ mRuntimePermissionsPersistence.onUserRemovedLPw(userId);
writePackageListLPr();
}
@@ -5108,7 +5108,7 @@
}
}
- private void onUserRemoved(int userId) {
+ private void onUserRemovedLPw(int userId) {
// Make sure we do not
mHandler.removeMessages(userId);
@@ -5119,6 +5119,9 @@
for (SettingBase sb : mSharedUsers.values()) {
revokeRuntimePermissionsAndClearFlags(sb, userId);
}
+
+ mDefaultPermissionsGranted.delete(userId);
+ mFingerprints.remove(userId);
}
private void revokeRuntimePermissionsAndClearFlags(SettingBase sb, int userId) {
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
index 76d47a8..df51923 100644
--- a/services/core/java/com/android/server/pm/ShortcutLauncher.java
+++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java
@@ -25,6 +25,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.ShortcutUser.PackageWithUser;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -36,6 +38,8 @@
/**
* Launcher information used by {@link ShortcutService}.
+ *
+ * All methods should be guarded by {@code #mShortcutUser.mService.mLock}.
*/
class ShortcutLauncher extends ShortcutPackageItem {
private static final String TAG = ShortcutService.TAG;
@@ -80,26 +84,31 @@
* Called when the new package can't receive the backup, due to signature or version mismatch.
*/
@Override
- protected void onRestoreBlocked(ShortcutService s) {
+ protected void onRestoreBlocked() {
final ArrayList<PackageWithUser> pinnedPackages =
new ArrayList<>(mPinnedShortcuts.keySet());
mPinnedShortcuts.clear();
for (int i = pinnedPackages.size() - 1; i >= 0; i--) {
final PackageWithUser pu = pinnedPackages.get(i);
- s.getPackageShortcutsLocked(pu.packageName, pu.userId)
- .refreshPinnedFlags(s);
+ final ShortcutPackage p = mShortcutUser.getPackageShortcutsIfExists(pu.packageName);
+ if (p != null) {
+ p.refreshPinnedFlags();
+ }
}
}
@Override
- protected void onRestored(ShortcutService s) {
+ protected void onRestored() {
// Nothing to do.
}
- public void pinShortcuts(@NonNull ShortcutService s, @UserIdInt int packageUserId,
+ public void pinShortcuts(@UserIdInt int packageUserId,
@NonNull String packageName, @NonNull List<String> ids) {
final ShortcutPackage packageShortcuts =
- s.getPackageShortcutsLocked(packageName, packageUserId);
+ mShortcutUser.getPackageShortcutsIfExists(packageName);
+ if (packageShortcuts == null) {
+ return; // No need to instantiate.
+ }
final PackageWithUser pu = PackageWithUser.of(packageUserId, packageName);
@@ -120,13 +129,14 @@
if (si == null) {
continue;
}
- if (si.isDynamic() || (prevSet != null && prevSet.contains(id))) {
+ if (si.isDynamic() || si.isManifestShortcut()
+ || (prevSet != null && prevSet.contains(id))) {
newSet.add(id);
}
}
mPinnedShortcuts.put(pu, newSet);
}
- packageShortcuts.refreshPinnedFlags(s);
+ packageShortcuts.refreshPinnedFlags();
}
/**
@@ -240,7 +250,7 @@
return ret;
}
- public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
pw.println();
pw.print(prefix);
@@ -252,7 +262,7 @@
pw.print(getOwnerUserId());
pw.println();
- getPackageInfo().dump(s, pw, prefix + " ");
+ getPackageInfo().dump(pw, prefix + " ");
pw.println();
final int size = mPinnedShortcuts.size();
@@ -280,6 +290,15 @@
}
}
+ @Override
+ public JSONObject dumpCheckin(boolean clear) throws JSONException {
+ final JSONObject result = super.dumpCheckin(clear);
+
+ // Nothing really interesting to dump.
+
+ return result;
+ }
+
@VisibleForTesting
ArraySet<String> getAllPinnedShortcutsForTest(String packageName, int packageUserId) {
return new ArraySet<>(mPinnedShortcuts.get(PackageWithUser.of(packageUserId, packageName)));
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 151f61e..827b88a 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -20,16 +20,25 @@
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.PackageInfo;
import android.content.pm.ShortcutInfo;
+import android.content.res.Resources;
+import android.os.Bundle;
import android.os.PersistableBundle;
import android.text.format.Formatter;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
+import com.android.server.pm.ShortcutService.ShortcutOperation;
+import com.android.server.pm.ShortcutService.Stats;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -38,35 +47,50 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
/**
* Package information used by {@link ShortcutService}.
+ * User information used by {@link ShortcutService}.
+ *
+ * All methods should be guarded by {@code #mShortcutUser.mService.mLock}.
*/
class ShortcutPackage extends ShortcutPackageItem {
private static final String TAG = ShortcutService.TAG;
+ private static final String TAG_VERIFY = ShortcutService.TAG + ".verify";
static final String TAG_ROOT = "package";
- private static final String TAG_INTENT_EXTRAS = "intent-extras";
+ private static final String TAG_INTENT_EXTRAS_LEGACY = "intent-extras";
+ private static final String TAG_INTENT = "intent";
private static final String TAG_EXTRAS = "extras";
private static final String TAG_SHORTCUT = "shortcut";
private static final String TAG_CATEGORIES = "categories";
private static final String ATTR_NAME = "name";
- private static final String ATTR_DYNAMIC_COUNT = "dynamic-count";
private static final String ATTR_CALL_COUNT = "call-count";
private static final String ATTR_LAST_RESET = "last-reset";
private static final String ATTR_ID = "id";
private static final String ATTR_ACTIVITY = "activity";
private static final String ATTR_TITLE = "title";
+ private static final String ATTR_TITLE_RES_ID = "titleid";
+ private static final String ATTR_TITLE_RES_NAME = "titlename";
private static final String ATTR_TEXT = "text";
- private static final String ATTR_INTENT = "intent";
- private static final String ATTR_WEIGHT = "weight";
+ private static final String ATTR_TEXT_RES_ID = "textid";
+ private static final String ATTR_TEXT_RES_NAME = "textname";
+ private static final String ATTR_DISABLED_MESSAGE = "dmessage";
+ private static final String ATTR_DISABLED_MESSAGE_RES_ID = "dmessageid";
+ private static final String ATTR_DISABLED_MESSAGE_RES_NAME = "dmessagename";
+ private static final String ATTR_INTENT_LEGACY = "intent";
+ private static final String ATTR_INTENT_NO_EXTRA = "intent-base";
+ private static final String ATTR_RANK = "rank";
private static final String ATTR_TIMESTAMP = "timestamp";
private static final String ATTR_FLAGS = "flags";
- private static final String ATTR_ICON_RES = "icon-res";
+ private static final String ATTR_ICON_RES_ID = "icon-res";
+ private static final String ATTR_ICON_RES_NAME = "icon-resname";
private static final String ATTR_BITMAP_PATH = "bitmap-path";
private static final String NAME_CATEGORIES = "categories";
@@ -74,17 +98,18 @@
private static final String TAG_STRING_ARRAY_XMLUTILS = "string-array";
private static final String ATTR_NAME_XMLUTILS = "name";
+ private static final String KEY_DYNAMIC = "dynamic";
+ private static final String KEY_MANIFEST = "manifest";
+ private static final String KEY_PINNED = "pinned";
+ private static final String KEY_BITMAPS = "bitmaps";
+ private static final String KEY_BITMAP_BYTES = "bitmapBytes";
+
/**
* All the shortcuts from the package, keyed on IDs.
*/
final private ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
/**
- * # of dynamic shortcuts.
- */
- private int mDynamicShortcutCount = 0;
-
- /**
* # of times the package has called rate-limited APIs.
*/
private int mApiCallCount;
@@ -98,17 +123,16 @@
private long mLastKnownForegroundElapsedTime;
- private ShortcutPackage(ShortcutService s, ShortcutUser shortcutUser,
+ private ShortcutPackage(ShortcutUser shortcutUser,
int packageUserId, String packageName, ShortcutPackageInfo spi) {
super(shortcutUser, packageUserId, packageName,
spi != null ? spi : ShortcutPackageInfo.newEmpty());
- mPackageUid = s.injectGetPackageUid(packageName, packageUserId);
+ mPackageUid = shortcutUser.mService.injectGetPackageUid(packageName, packageUserId);
}
- public ShortcutPackage(ShortcutService s, ShortcutUser shortcutUser,
- int packageUserId, String packageName) {
- this(s, shortcutUser, packageUserId, packageName, null);
+ public ShortcutPackage(ShortcutUser shortcutUser, int packageUserId, String packageName) {
+ this(shortcutUser, packageUserId, packageName, null);
}
@Override
@@ -122,37 +146,46 @@
}
/**
- * Called when a shortcut is about to be published. At this point we know the publisher package
+ * Called when a shortcut is about to be published. At this point we know the publisher
+ * package
* exists (as opposed to Launcher trying to fetch shortcuts from a non-existent package), so
* we do some initialization for the package.
*/
- private void onShortcutPublish(ShortcutService s) {
+ private void ensurePackageVersionInfo() {
// Make sure we have the version code for the app. We need the version code in
// handlePackageUpdated().
if (getPackageInfo().getVersionCode() < 0) {
- final int versionCode = s.getApplicationVersionCode(getPackageName(), getOwnerUserId());
- if (ShortcutService.DEBUG) {
- Slog.d(TAG, String.format("Package %s version = %d", getPackageName(),
- versionCode));
- }
- if (versionCode >= 0) {
- getPackageInfo().setVersionCode(versionCode);
+ final ShortcutService s = mShortcutUser.mService;
+
+ final PackageInfo pi = s.getPackageInfo(getPackageName(), getOwnerUserId());
+ if (pi != null) {
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, String.format("Package %s version = %d", getPackageName(),
+ pi.versionCode));
+ }
+ getPackageInfo().updateVersionInfo(pi);
s.scheduleSaveUser(getOwnerUserId());
}
}
}
+ @Nullable
+ public Resources getPackageResources() {
+ return mShortcutUser.mService.injectGetResourcesForApplicationAsUser(
+ getPackageName(), getPackageUserId());
+ }
+
@Override
- protected void onRestoreBlocked(ShortcutService s) {
+ protected void onRestoreBlocked() {
// Can't restore due to version/signature mismatch. Remove all shortcuts.
mShortcuts.clear();
}
@Override
- protected void onRestored(ShortcutService s) {
+ protected void onRestored() {
// Because some launchers may not have been restored (e.g. allowBackup=false),
// we need to re-calculate the pinned shortcuts.
- refreshPinnedFlags(s);
+ refreshPinnedFlags();
}
/**
@@ -163,19 +196,48 @@
return mShortcuts.get(id);
}
- private ShortcutInfo deleteShortcut(@NonNull ShortcutService s,
- @NonNull String id) {
+ private void ensureNotImmutable(@Nullable ShortcutInfo shortcut) {
+ if (shortcut != null && shortcut.isImmutable()) {
+ throw new IllegalArgumentException(
+ "Manifest shortcut ID=" + shortcut.getId()
+ + " may not be manipulated via APIs");
+ }
+ }
+
+ private void ensureNotImmutable(@NonNull String id) {
+ ensureNotImmutable(mShortcuts.get(id));
+ }
+
+ public void ensureImmutableShortcutsNotIncludedWithIds(@NonNull List<String> shortcutIds) {
+ for (int i = shortcutIds.size() - 1; i >= 0; i--) {
+ ensureNotImmutable(shortcutIds.get(i));
+ }
+ }
+
+ public void ensureImmutableShortcutsNotIncluded(@NonNull List<ShortcutInfo> shortcuts) {
+ for (int i = shortcuts.size() - 1; i >= 0; i--) {
+ ensureNotImmutable(shortcuts.get(i).getId());
+ }
+ }
+
+ private ShortcutInfo deleteShortcutInner(@NonNull String id) {
final ShortcutInfo shortcut = mShortcuts.remove(id);
if (shortcut != null) {
- s.removeIcon(getPackageUserId(), shortcut);
- shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED);
+ mShortcutUser.mService.removeIcon(getPackageUserId(), shortcut);
+ shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED
+ | ShortcutInfo.FLAG_MANIFEST);
}
return shortcut;
}
- void addShortcut(@NonNull ShortcutService s, @NonNull ShortcutInfo newShortcut) {
- deleteShortcut(s, newShortcut.getId());
+ private void addShortcutInner(@NonNull ShortcutInfo newShortcut) {
+ final ShortcutService s = mShortcutUser.mService;
+
+ deleteShortcutInner(newShortcut.getId());
+
+ // Extract Icon and update the icon res ID and the bitmap path.
s.saveIconAndFixUpShortcut(getPackageUserId(), newShortcut);
+ s.fixUpShortcutResourceNamesAndValues(newShortcut);
mShortcuts.put(newShortcut.getId(), newShortcut);
}
@@ -184,52 +246,47 @@
*
* It checks the max number of dynamic shortcuts.
*/
- public void addDynamicShortcut(@NonNull ShortcutService s,
- @NonNull ShortcutInfo newShortcut) {
+ public void addOrUpdateDynamicShortcut(@NonNull ShortcutInfo newShortcut) {
- onShortcutPublish(s);
+ Preconditions.checkArgument(newShortcut.isEnabled(),
+ "add/setDynamicShortcuts() cannot publish disabled shortcuts");
+
+ ensurePackageVersionInfo();
newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
final boolean wasPinned;
- final int newDynamicCount;
if (oldShortcut == null) {
wasPinned = false;
- newDynamicCount = mDynamicShortcutCount + 1; // adding a dynamic shortcut.
} else {
+ // It's an update case.
+ // Make sure the target is updatable. (i.e. should be mutable.)
+ oldShortcut.ensureUpdatableWith(newShortcut);
+
wasPinned = oldShortcut.isPinned();
- if (oldShortcut.isDynamic()) {
- newDynamicCount = mDynamicShortcutCount; // not adding a dynamic shortcut.
- } else {
- newDynamicCount = mDynamicShortcutCount + 1; // adding a dynamic shortcut.
- }
}
- // Make sure there's still room.
- s.enforceMaxDynamicShortcuts(newDynamicCount);
-
- // Okay, make it dynamic and add.
+ // If it was originally pinned, the new one should be pinned too.
if (wasPinned) {
newShortcut.addFlags(ShortcutInfo.FLAG_PINNED);
}
- addShortcut(s, newShortcut);
- mDynamicShortcutCount = newDynamicCount;
+ addShortcutInner(newShortcut);
}
/**
* Remove all shortcuts that aren't pinned nor dynamic.
*/
- private void removeOrphans(@NonNull ShortcutService s) {
+ private void removeOrphans() {
ArrayList<String> removeList = null; // Lazily initialize.
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
final ShortcutInfo si = mShortcuts.valueAt(i);
- if (si.isPinned() || si.isDynamic()) continue;
+ if (si.isAlive()) continue;
if (removeList == null) {
removeList = new ArrayList<>();
@@ -238,7 +295,7 @@
}
if (removeList != null) {
for (int i = removeList.size() - 1; i >= 0; i--) {
- deleteShortcut(s, removeList.get(i));
+ deleteShortcutInner(removeList.get(i));
}
}
}
@@ -246,30 +303,103 @@
/**
* Remove all dynamic shortcuts.
*/
- public void deleteAllDynamicShortcuts(@NonNull ShortcutService s) {
+ public void deleteAllDynamicShortcuts() {
+ final long now = mShortcutUser.mService.injectCurrentTimeMillis();
+
+ boolean changed = false;
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
- mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_DYNAMIC);
+ final ShortcutInfo si = mShortcuts.valueAt(i);
+ if (si.isDynamic()) {
+ changed = true;
+
+ si.setTimestamp(now);
+ si.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
+ si.setRank(0); // It may still be pinned, so clear the rank.
+ }
}
- removeOrphans(s);
- mDynamicShortcutCount = 0;
+ if (changed) {
+ removeOrphans();
+ }
}
/**
- * Remove a dynamic shortcut by ID.
+ * Remove a dynamic shortcut by ID. It'll be removed from the dynamic set, but if the shortcut
+ * is pinned, it'll remain as a pinned shortcut, and is still enabled.
+ *
+ * @return true if it's actually removed because it wasn't pinned, or false if it's still
+ * pinned.
*/
- public void deleteDynamicWithId(@NonNull ShortcutService s, @NonNull String shortcutId) {
+ public boolean deleteDynamicWithId(@NonNull String shortcutId) {
+ final ShortcutInfo removed = deleteOrDisableWithId(
+ shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false);
+ return removed == null;
+ }
+
+ /**
+ * Disable a dynamic shortcut by ID. It'll be removed from the dynamic set, but if the shortcut
+ * is pinned, it'll remain as a pinned shortcut, but will be disabled.
+ *
+ * @return true if it's actually removed because it wasn't pinned, or false if it's still
+ * pinned.
+ */
+ private boolean disableDynamicWithId(@NonNull String shortcutId) {
+ final ShortcutInfo disabled = deleteOrDisableWithId(
+ shortcutId, /* disable =*/ true, /* overrideImmutable=*/ false);
+ return disabled == null;
+ }
+
+ /**
+ * Disable a dynamic shortcut by ID. It'll be removed from the dynamic set, but if the shortcut
+ * is pinned, it'll remain as a pinned shortcut but will be disabled.
+ */
+ public void disableWithId(@NonNull String shortcutId, String disabledMessage,
+ int disabledMessageResId, boolean overrideImmutable) {
+ final ShortcutInfo disabled = deleteOrDisableWithId(shortcutId, /* disable =*/ true,
+ overrideImmutable);
+
+ if (disabled != null) {
+ if (disabledMessage != null) {
+ disabled.setDisabledMessage(disabledMessage);
+ } else if (disabledMessageResId != 0) {
+ disabled.setDisabledMessageResId(disabledMessageResId);
+
+ mShortcutUser.mService.fixUpShortcutResourceNamesAndValues(disabled);
+ }
+ }
+ }
+
+ @Nullable
+ private ShortcutInfo deleteOrDisableWithId(@NonNull String shortcutId, boolean disable,
+ boolean overrideImmutable) {
final ShortcutInfo oldShortcut = mShortcuts.get(shortcutId);
- if (oldShortcut == null) {
- return;
+ if (oldShortcut == null || !oldShortcut.isEnabled()) {
+ return null; // Doesn't exist or already disabled.
}
- if (oldShortcut.isDynamic()) {
- mDynamicShortcutCount--;
+ if (!overrideImmutable) {
+ ensureNotImmutable(oldShortcut);
}
if (oldShortcut.isPinned()) {
- oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
+
+ oldShortcut.setRank(0);
+ oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_MANIFEST);
+ if (disable) {
+ oldShortcut.addFlags(ShortcutInfo.FLAG_DISABLED);
+ }
+ oldShortcut.setTimestamp(mShortcutUser.mService.injectCurrentTimeMillis());
+
+ return oldShortcut;
} else {
- deleteShortcut(s, shortcutId);
+ deleteShortcutInner(shortcutId);
+ return null;
+ }
+ }
+
+ public void enableWithId(@NonNull String shortcutId) {
+ final ShortcutInfo shortcut = mShortcuts.get(shortcutId);
+ if (shortcut != null) {
+ ensureNotImmutable(shortcut);
+ shortcut.clearFlags(ShortcutInfo.FLAG_DISABLED);
}
}
@@ -279,14 +409,15 @@
*
* <p>Then remove all shortcuts that are not dynamic and no longer pinned either.
*/
- public void refreshPinnedFlags(@NonNull ShortcutService s) {
+ public void refreshPinnedFlags() {
// First, un-pin all shortcuts
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_PINNED);
}
// Then, for the pinned set for each launcher, set the pin flag one by one.
- s.getUserShortcutsLocked(getPackageUserId()).forAllLaunchers(launcherShortcuts -> {
+ mShortcutUser.mService.getUserShortcutsLocked(getPackageUserId())
+ .forAllLaunchers(launcherShortcuts -> {
final ArraySet<String> pinned = launcherShortcuts.getPinnedShortcutIds(
getPackageName(), getPackageUserId());
@@ -308,7 +439,7 @@
});
// Lastly, remove the ones that are no longer pinned nor dynamic.
- removeOrphans(s);
+ removeOrphans();
}
/**
@@ -317,8 +448,8 @@
* <p>This takes care of the resetting the counter for foreground apps as well as after
* locale changes.
*/
- public int getApiCallCount(@NonNull ShortcutService s) {
- mShortcutUser.resetThrottlingIfNeeded(s);
+ public int getApiCallCount() {
+ final ShortcutService s = mShortcutUser.mService;
// Reset the counter if:
// - the package is in foreground now.
@@ -328,7 +459,7 @@
|| mLastKnownForegroundElapsedTime
< s.getUidLastForegroundElapsedTimeLocked(mPackageUid)) {
mLastKnownForegroundElapsedTime = s.injectElapsedRealtime();
- resetRateLimiting(s);
+ resetRateLimiting();
}
// Note resetThrottlingIfNeeded() and resetRateLimiting() will set 0 to mApiCallCount,
@@ -349,8 +480,8 @@
// If not reset yet, then reset.
if (mLastResetTime < last) {
if (ShortcutService.DEBUG) {
- Slog.d(TAG, String.format("My last reset=%d, now=%d, last=%d: resetting",
- mLastResetTime, now, last));
+ Slog.d(TAG, String.format("%s: last reset=%d, now=%d, last=%d: resetting",
+ getPackageName(), mLastResetTime, now, last));
}
mApiCallCount = 0;
mLastResetTime = last;
@@ -365,8 +496,10 @@
* <p>This takes care of the resetting the counter for foreground apps as well as after
* locale changes, which is done internally by {@link #getApiCallCount}.
*/
- public boolean tryApiCall(@NonNull ShortcutService s) {
- if (getApiCallCount(s) >= s.mMaxUpdatesPerInterval) {
+ public boolean tryApiCall() {
+ final ShortcutService s = mShortcutUser.mService;
+
+ if (getApiCallCount() >= s.mMaxUpdatesPerInterval) {
return false;
}
mApiCallCount++;
@@ -374,13 +507,13 @@
return true;
}
- public void resetRateLimiting(@NonNull ShortcutService s) {
+ public void resetRateLimiting() {
if (ShortcutService.DEBUG) {
Slog.d(TAG, "resetRateLimiting: " + getPackageName());
}
if (mApiCallCount > 0) {
mApiCallCount = 0;
- s.scheduleSaveUser(getOwnerUserId());
+ mShortcutUser.mService.scheduleSaveUser(getOwnerUserId());
}
}
@@ -392,9 +525,9 @@
/**
* Find all shortcuts that match {@code query}.
*/
- public void findAll(@NonNull ShortcutService s, @NonNull List<ShortcutInfo> result,
+ public void findAll(@NonNull List<ShortcutInfo> result,
@Nullable Predicate<ShortcutInfo> query, int cloneFlag) {
- findAll(s, result, query, cloneFlag, null, 0);
+ findAll(result, query, cloneFlag, null, 0);
}
/**
@@ -404,7 +537,7 @@
* by the calling launcher will not be included in the result, and also "isPinned" will be
* adjusted for the caller too.
*/
- public void findAll(@NonNull ShortcutService s, @NonNull List<ShortcutInfo> result,
+ public void findAll(@NonNull List<ShortcutInfo> result,
@Nullable Predicate<ShortcutInfo> query, int cloneFlag,
@Nullable String callingLauncher, int launcherUserId) {
if (getPackageInfo().isShadow()) {
@@ -412,6 +545,8 @@
return;
}
+ final ShortcutService s = mShortcutUser.mService;
+
// Set of pinned shortcuts by the calling launcher.
final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
: s.getLauncherShortcutsLocked(callingLauncher, getPackageUserId(), launcherUserId)
@@ -420,22 +555,19 @@
for (int i = 0; i < mShortcuts.size(); i++) {
final ShortcutInfo si = mShortcuts.valueAt(i);
- // If it's called by non-launcher (i.e. publisher, always include -> true.
- // Otherwise, only include non-dynamic pinned one, if the calling launcher has pinned
- // it.
+ // Need to adjust PINNED flag depending on the caller.
+ // Basically if the caller is a launcher (callingLauncher != null) and the launcher
+ // isn't pinning it, then we need to clear PINNED for this caller.
final boolean isPinnedByCaller = (callingLauncher == null)
|| ((pinnedByCallerSet != null) && pinnedByCallerSet.contains(si.getId()));
- if (!si.isDynamic()) {
- if (!si.isPinned()) {
- s.wtf("Shortcut not pinned: package " + getPackageName()
- + ", user=" + getPackageUserId() + ", id=" + si.getId());
- continue;
- }
+
+ if (si.isFloating()) {
if (!isPinnedByCaller) {
continue;
}
}
final ShortcutInfo clone = si.clone(cloneFlag);
+
// Fix up isPinned for the caller. Note we need to do it before the "test" callback,
// since it may check isPinned.
if (!isPinnedByCaller) {
@@ -452,30 +584,180 @@
}
/**
- * Called when the package is updated. If there are shortcuts with resource icons, update
- * their timestamps.
+ * Return the filenames (excluding path names) of icon bitmap files from this package.
*/
- public void handlePackageUpdated(ShortcutService s, int newVersionCode) {
- if (getPackageInfo().getVersionCode() >= newVersionCode) {
- // Version hasn't changed; nothing to do.
- return;
- }
- if (ShortcutService.DEBUG) {
- Slog.d(TAG, String.format("Package %s updated, version %d -> %d", getPackageName(),
- getPackageInfo().getVersionCode(), newVersionCode));
- }
+ public ArraySet<String> getUsedBitmapFiles() {
+ final ArraySet<String> usedFiles = new ArraySet<>(mShortcuts.size());
- getPackageInfo().setVersionCode(newVersionCode);
-
- boolean changed = false;
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
final ShortcutInfo si = mShortcuts.valueAt(i);
-
- if (si.hasIconResource()) {
- changed = true;
- si.setTimestamp(s.injectCurrentTimeMillis());
+ if (si.getBitmapPath() != null) {
+ usedFiles.add(getFileName(si.getBitmapPath()));
}
}
+ return usedFiles;
+ }
+
+ private static String getFileName(@NonNull String path) {
+ final int sep = path.lastIndexOf(File.separatorChar);
+ if (sep == -1) {
+ return path;
+ } else {
+ return path.substring(sep + 1);
+ }
+ }
+
+ /**
+ * @return false if any of the target activities are no longer enabled.
+ */
+ private boolean areAllActivitiesStillEnabled() {
+ if (mShortcuts.size() == 0) {
+ return true;
+ }
+ final ShortcutService s = mShortcutUser.mService;
+
+ // Normally the number of target activities is 1 or so, so no need to use a complex
+ // structure like a set.
+ final ArrayList<ComponentName> checked = new ArrayList<>(4);
+
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ final ShortcutInfo si = mShortcuts.valueAt(i);
+ final ComponentName activity = si.getActivity();
+
+ if (checked.contains(activity)) {
+ continue; // Already checked.
+ }
+ checked.add(activity);
+
+ if (!s.injectIsActivityEnabledAndExported(activity, getOwnerUserId())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Called when the package may be added or updated, or its activities may be disabled, and
+ * if so, rescan the package and do the necessary stuff.
+ *
+ * Add case:
+ * - Publish manifest shortcuts.
+ *
+ * Update case:
+ * - Re-publish manifest shortcuts.
+ * - If there are shortcuts with resources (icons or strings), update their timestamps.
+ * - Disable shortcuts whose target activities are disabled.
+ *
+ * @return TRUE if any shortcuts have been changed.
+ */
+ public boolean rescanPackageIfNeeded(boolean isNewApp, boolean forceRescan) {
+ final ShortcutService s = mShortcutUser.mService;
+ final long start = s.injectElapsedRealtime();
+
+ final PackageInfo pi;
+ try {
+ pi = mShortcutUser.mService.getPackageInfo(
+ getPackageName(), getPackageUserId());
+ if (pi == null) {
+ return false; // Shouldn't happen.
+ }
+
+ if (!isNewApp && !forceRescan) {
+ // Return if the package hasn't changed, ie:
+ // - version code hasn't change
+ // - lastUpdateTime hasn't change
+ // - all target activities are still enabled.
+ if ((getPackageInfo().getVersionCode() == pi.versionCode)
+ && (getPackageInfo().getLastUpdateTime() == pi.lastUpdateTime)
+ && areAllActivitiesStillEnabled()) {
+ return false;
+ }
+ }
+ } finally {
+ s.logDurationStat(Stats.PACKAGE_UPDATE_CHECK, start);
+ }
+
+ // Now prepare to publish manifest shortcuts.
+ List<ShortcutInfo> newManifestShortcutList = null;
+ try {
+ newManifestShortcutList = ShortcutParser.parseShortcuts(mShortcutUser.mService,
+ getPackageName(), getPackageUserId());
+ } catch (IOException|XmlPullParserException e) {
+ Slog.e(TAG, "Failed to load shortcuts from AndroidManifest.xml.", e);
+ }
+ final int manifestShortcutSize = newManifestShortcutList == null ? 0
+ : newManifestShortcutList.size();
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, String.format("Package %s has %d manifest shortcut(s)",
+ getPackageName(), manifestShortcutSize));
+ }
+ if (isNewApp && (manifestShortcutSize == 0)) {
+ // If it's a new app, and it doesn't have manifest shortcuts, then nothing to do.
+
+ // If it's an update, then it may already have manifest shortcuts, which need to be
+ // disabled.
+ return false;
+ }
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, String.format("Package %s %s, version %d -> %d", getPackageName(),
+ (isNewApp ? "added" : "updated"),
+ getPackageInfo().getVersionCode(), pi.versionCode));
+ }
+
+ getPackageInfo().updateVersionInfo(pi);
+
+ boolean changed = false;
+
+ // For existing shortcuts, update timestamps if they have any resources.
+ // Also check if shortcuts' activities are still main activities. Otherwise, disable them.
+ if (!isNewApp) {
+ Resources publisherRes = null;
+
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ final ShortcutInfo si = mShortcuts.valueAt(i);
+
+ if (si.isDynamic()) {
+ if (!s.injectIsMainActivity(si.getActivity(), getPackageUserId())) {
+ Slog.w(TAG, String.format(
+ "%s is no longer main activity. Disabling shorcut %s.",
+ getPackageName(), si.getId()));
+ if (disableDynamicWithId(si.getId())) {
+ continue; // Actually removed.
+ }
+ // Still pinned, so fall-through and possibly update the resources.
+ }
+ changed = true;
+ }
+
+ if (si.hasAnyResources()) {
+ if (!si.isOriginallyFromManifest()) {
+ if (publisherRes == null) {
+ publisherRes = getPackageResources();
+ if (publisherRes == null) {
+ break; // Resources couldn't be loaded.
+ }
+ }
+
+ // If this shortcut is not from a manifest, then update all resource IDs
+ // from resource names. (We don't allow resource strings for
+ // non-manifest at the moment, but icons can still be resources.)
+ si.lookupAndFillInResourceIds(publisherRes);
+ }
+ changed = true;
+ si.setTimestamp(s.injectCurrentTimeMillis());
+ }
+ }
+ }
+
+ // (Re-)publish manifest shortcut.
+ changed |= publishManifestShortcuts(newManifestShortcutList);
+
+ if (newManifestShortcutList != null) {
+ changed |= pushOutExcessShortcuts();
+ }
+
+ s.verifyStates();
+
if (changed) {
// This will send a notification to the launcher, and also save .
s.packageShortcutsChanged(getPackageName(), getPackageUserId());
@@ -483,9 +765,387 @@
// Still save the version code.
s.scheduleSaveUser(getPackageUserId());
}
+ return changed;
}
- public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
+ private boolean publishManifestShortcuts(List<ShortcutInfo> newManifestShortcutList) {
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, String.format(
+ "Package %s: publishing manifest shortcuts", getPackageName()));
+ }
+ boolean changed = false;
+
+ // Keep the previous IDs.
+ ArraySet<String> toDisableList = null;
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ final ShortcutInfo si = mShortcuts.valueAt(i);
+
+ if (si.isManifestShortcut()) {
+ if (toDisableList == null) {
+ toDisableList = new ArraySet<>();
+ }
+ toDisableList.add(si.getId());
+ }
+ }
+
+ // Publish new ones.
+ if (newManifestShortcutList != null) {
+ final int newListSize = newManifestShortcutList.size();
+
+ for (int i = 0; i < newListSize; i++) {
+ changed = true;
+
+ final ShortcutInfo newShortcut = newManifestShortcutList.get(i);
+ final boolean newDisabled = !newShortcut.isEnabled();
+
+ final String id = newShortcut.getId();
+ final ShortcutInfo oldShortcut = mShortcuts.get(id);
+
+ boolean wasPinned = false;
+
+ if (oldShortcut != null) {
+ if (!oldShortcut.isOriginallyFromManifest()) {
+ Slog.e(TAG, "Shortcut with ID=" + newShortcut.getId()
+ + " exists but is not from AndroidManifest.xml, not updating.");
+ continue;
+ }
+ // Take over the pinned flag.
+ if (oldShortcut.isPinned()) {
+ wasPinned = true;
+ newShortcut.addFlags(ShortcutInfo.FLAG_PINNED);
+ }
+ }
+ if (newDisabled && !wasPinned) {
+ // If the shortcut is disabled, and it was *not* pinned, then this
+ // just doesn't have to be published.
+ // Just keep it in toDisableList, so the previous one would be removed.
+ continue;
+ }
+
+ // Note even if enabled=false, we still need to update all fields, so do it
+ // regardless.
+ addShortcutInner(newShortcut); // This will clean up the old one too.
+
+ if (!newDisabled && toDisableList != null) {
+ // Still alive, don't remove.
+ toDisableList.remove(id);
+ }
+ }
+ }
+
+ // Disable the previous manifest shortcuts that are no longer in the manifest.
+ if (toDisableList != null) {
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, String.format(
+ "Package %s: disabling %d stale shortcuts", getPackageName(),
+ toDisableList.size()));
+ }
+ for (int i = toDisableList.size() - 1; i >= 0; i--) {
+ changed = true;
+
+ final String id = toDisableList.valueAt(i);
+
+ disableWithId(id, /* disable message =*/ null, /* disable message resid */ 0,
+ /* overrideImmutable=*/ true);
+ }
+ removeOrphans();
+ }
+ adjustRanks();
+ return changed;
+ }
+
+ /**
+ * For each target activity, make sure # of dynamic + manifest shortcuts <= max.
+ * If too many, we'll remove the dynamic with the lowest ranks.
+ */
+ private boolean pushOutExcessShortcuts() {
+ final ShortcutService service = mShortcutUser.mService;
+ final int maxShortcuts = service.getMaxActivityShortcuts();
+
+ boolean changed = false;
+
+ final ArrayMap<ComponentName, ArrayList<ShortcutInfo>> all =
+ sortShortcutsToActivities();
+ for (int outer = all.size() - 1; outer >= 0; outer--) {
+ final ArrayList<ShortcutInfo> list = all.valueAt(outer);
+ if (list.size() <= maxShortcuts) {
+ continue;
+ }
+ // Sort by isManifestShortcut() and getRank().
+ Collections.sort(list, mShortcutTypeAndRankComparator);
+
+ // Keep [0 .. max), and remove (as dynamic) [max .. size)
+ for (int inner = list.size() - 1; inner >= maxShortcuts; inner--) {
+ final ShortcutInfo shortcut = list.get(inner);
+
+ if (shortcut.isManifestShortcut()) {
+ // This shouldn't happen -- excess shortcuts should all be non-manifest.
+ // But just in case.
+ service.wtf("Found manifest shortcuts in excess list.");
+ continue;
+ }
+ deleteDynamicWithId(shortcut.getId());
+ }
+ }
+
+ return changed;
+ }
+
+ /**
+ * To sort by isManifestShortcut() and getRank(). i.e. manifest shortcuts come before
+ * non-manifest shortcuts, then sort by rank.
+ *
+ * This is used to decide which dynamic shortcuts to remove when an upgraded version has more
+ * manifest shortcuts than before and as a result we need to remove some of the dynamic
+ * shortcuts. We sort manifest + dynamic shortcuts by this order, and remove the ones with
+ * the last ones.
+ *
+ * (Note the number of manifest shortcuts is always <= the max number, because if there are
+ * more, ShortcutParser would ignore the rest.)
+ */
+ final Comparator<ShortcutInfo> mShortcutTypeAndRankComparator = (ShortcutInfo a,
+ ShortcutInfo b) -> {
+ if (a.isManifestShortcut() && !b.isManifestShortcut()) {
+ return -1;
+ }
+ if (!a.isManifestShortcut() && b.isManifestShortcut()) {
+ return 1;
+ }
+ return Integer.compare(a.getRank(), b.getRank());
+ };
+
+ /**
+ * Build a list of shortcuts for each target activity and return as a map. The result won't
+ * contain "floating" shortcuts because they don't belong on any activities.
+ */
+ private ArrayMap<ComponentName, ArrayList<ShortcutInfo>> sortShortcutsToActivities() {
+ final ArrayMap<ComponentName, ArrayList<ShortcutInfo>> activitiesToShortcuts
+ = new ArrayMap<>();
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ final ShortcutInfo si = mShortcuts.valueAt(i);
+ if (si.isFloating()) {
+ continue; // Ignore floating shortcuts, which are not tied to any activities.
+ }
+
+ final ComponentName activity = si.getActivity();
+
+ ArrayList<ShortcutInfo> list = activitiesToShortcuts.get(activity);
+ if (list == null) {
+ list = new ArrayList<>();
+ activitiesToShortcuts.put(activity, list);
+ }
+ list.add(si);
+ }
+ return activitiesToShortcuts;
+ }
+
+ /** Used by {@link #enforceShortcutCountsBeforeOperation} */
+ private void incrementCountForActivity(ArrayMap<ComponentName, Integer> counts,
+ ComponentName cn, int increment) {
+ Integer oldValue = counts.get(cn);
+ if (oldValue == null) {
+ oldValue = 0;
+ }
+
+ counts.put(cn, oldValue + increment);
+ }
+
+ /**
+ * Called by
+ * {@link android.content.pm.ShortcutManager#setDynamicShortcuts},
+ * {@link android.content.pm.ShortcutManager#addDynamicShortcuts}, and
+ * {@link android.content.pm.ShortcutManager#updateShortcuts} before actually performing
+ * the operation to make sure the operation wouldn't result in the target activities having
+ * more than the allowed number of dynamic/manifest shortcuts.
+ *
+ * @param newList shortcut list passed to set, add or updateShortcuts().
+ * @param operation add, set or update.
+ * @throws IllegalArgumentException if the operation would result in going over the max
+ * shortcut count for any activity.
+ */
+ public void enforceShortcutCountsBeforeOperation(List<ShortcutInfo> newList,
+ @ShortcutOperation int operation) {
+ final ShortcutService service = mShortcutUser.mService;
+
+ // Current # of dynamic / manifest shortcuts for each activity.
+ // (If it's for update, then don't count dynamic shortcuts, since they'll be replaced
+ // anyway.)
+ final ArrayMap<ComponentName, Integer> counts = new ArrayMap<>(4);
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ final ShortcutInfo shortcut = mShortcuts.valueAt(i);
+
+ if (shortcut.isManifestShortcut()) {
+ incrementCountForActivity(counts, shortcut.getActivity(), 1);
+ } else if (shortcut.isDynamic() && (operation != ShortcutService.OPERATION_SET)) {
+ incrementCountForActivity(counts, shortcut.getActivity(), 1);
+ }
+ }
+
+ for (int i = newList.size() - 1; i >= 0; i--) {
+ final ShortcutInfo newShortcut = newList.get(i);
+ final ComponentName newActivity = newShortcut.getActivity();
+ if (newActivity == null) {
+ if (operation != ShortcutService.OPERATION_UPDATE) {
+ service.wtf("Activity must not be null at this point");
+ continue; // Just ignore this invalid case.
+ }
+ continue; // Activity can be null for update.
+ }
+
+ final ShortcutInfo original = mShortcuts.get(newShortcut.getId());
+ if (original == null) {
+ if (operation == ShortcutService.OPERATION_UPDATE) {
+ continue; // When updating, ignore if there's no target.
+ }
+ // Add() or set(), and there's no existing shortcut with the same ID. We're
+ // simply publishing (as opposed to updating) this shortcut, so just +1.
+ incrementCountForActivity(counts, newActivity, 1);
+ continue;
+ }
+ if (original.isFloating() && (operation == ShortcutService.OPERATION_UPDATE)) {
+ // Updating floating shortcuts doesn't affect the count, so ignore.
+ continue;
+ }
+
+ // If it's add() or update(), then need to decrement for the previous activity.
+ // Skip it for set() since it's already been taken care of by not counting the original
+ // dynamic shortcuts in the first loop.
+ if (operation != ShortcutService.OPERATION_SET) {
+ final ComponentName oldActivity = original.getActivity();
+ if (!original.isFloating()) {
+ incrementCountForActivity(counts, oldActivity, -1);
+ }
+ }
+ incrementCountForActivity(counts, newActivity, 1);
+ }
+
+ // Then make sure none of the activities have more than the max number of shortcuts.
+ for (int i = counts.size() - 1; i >= 0; i--) {
+ service.enforceMaxActivityShortcuts(counts.valueAt(i));
+ }
+ }
+
+ /**
+ * For all the text fields, refresh the string values if they're from resources.
+ */
+ public void resolveResourceStrings() {
+ final ShortcutService s = mShortcutUser.mService;
+ boolean changed = false;
+
+ Resources publisherRes = null;
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ final ShortcutInfo si = mShortcuts.valueAt(i);
+
+ if (si.hasStringResources()) {
+ changed = true;
+
+ if (publisherRes == null) {
+ publisherRes = getPackageResources();
+ if (publisherRes == null) {
+ break; // Resources couldn't be loaded.
+ }
+ }
+
+ si.resolveResourceStrings(publisherRes);
+ si.setTimestamp(s.injectCurrentTimeMillis());
+ }
+ }
+ if (changed) {
+ s.packageShortcutsChanged(getPackageName(), getPackageUserId());
+ }
+ }
+
+ /** Clears the implicit ranks for all shortcuts. */
+ public void clearAllImplicitRanks() {
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ final ShortcutInfo si = mShortcuts.valueAt(i);
+ si.clearImplicitRankAndRankChangedFlag();
+ }
+ }
+
+ /**
+ * Used to sort shortcuts for rank auto-adjusting.
+ */
+ final Comparator<ShortcutInfo> mShortcutRankComparator = (ShortcutInfo a, ShortcutInfo b) -> {
+ // First, sort by rank.
+ int ret = Integer.compare(a.getRank(), b.getRank());
+ if (ret != 0) {
+ return ret;
+ }
+ // When ranks are tie, then prioritize the ones that have just been assigned new ranks.
+ // e.g. when there are 3 shortcuts, "s1" "s2" and "s3" with rank 0, 1, 2 respectively,
+ // adding a shortcut "s4" with rank 1 will "insert" it between "s1" and "s2", because
+ // "s2" and "s4" have the same rank 1 but s4 has isRankChanged() set.
+ // Similarly, updating s3's rank to 1 will insert it between s1 and s2.
+ if (a.isRankChanged() != b.isRankChanged()) {
+ return a.isRankChanged() ? -1 : 1;
+ }
+ // If they're still tie, sort by implicit rank -- i.e. preserve the order in which
+ // they're passed to the API.
+ ret = Integer.compare(a.getImplicitRank(), b.getImplicitRank());
+ if (ret != 0) {
+ return ret;
+ }
+ // If they're stil tie, just sort by their IDs.
+ // This may happen with updateShortcuts() -- see
+ // the testUpdateShortcuts_noManifestShortcuts() test.
+ return a.getId().compareTo(b.getId());
+ };
+
+ /**
+ * Re-calculate the ranks for all shortcuts.
+ */
+ public void adjustRanks() {
+ final ShortcutService s = mShortcutUser.mService;
+ final long now = s.injectCurrentTimeMillis();
+
+ // First, clear ranks for floating shortcuts.
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ final ShortcutInfo si = mShortcuts.valueAt(i);
+ if (si.isFloating()) {
+ if (si.getRank() != 0) {
+ si.setTimestamp(now);
+ si.setRank(0);
+ }
+ }
+ }
+
+ // Then adjust ranks. Ranks are unique for each activity, so we first need to sort
+ // shortcuts to each activity.
+ // Then sort the shortcuts within each activity with mShortcutRankComparator, and
+ // assign ranks from 0.
+ final ArrayMap<ComponentName, ArrayList<ShortcutInfo>> all =
+ sortShortcutsToActivities();
+ for (int outer = all.size() - 1; outer >= 0; outer--) { // For each activity.
+ final ArrayList<ShortcutInfo> list = all.valueAt(outer);
+
+ // Sort by ranks and other signals.
+ Collections.sort(list, mShortcutRankComparator);
+
+ int rank = 0;
+
+ final int size = list.size();
+ for (int i = 0; i < size; i++) {
+ final ShortcutInfo si = list.get(i);
+ if (si.isManifestShortcut()) {
+ // Don't adjust ranks for manifest shortcuts.
+ continue;
+ }
+ // At this point, it must be dynamic.
+ if (!si.isDynamic()) {
+ s.wtf("Non-dynamic shortcut found.");
+ continue;
+ }
+ final int thisRank = rank++;
+ if (si.getRank() != thisRank) {
+ si.setTimestamp(now);
+ si.setRank(thisRank);
+ }
+ }
+ }
+ }
+
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
pw.println();
pw.print(prefix);
@@ -498,7 +1158,7 @@
pw.print(prefix);
pw.print(" ");
pw.print("Calls: ");
- pw.print(getApiCallCount(s));
+ pw.print(getApiCallCount());
pw.println();
// getApiCallCount() may have updated mLastKnownForegroundElapsedTime.
@@ -514,10 +1174,10 @@
pw.print("Last reset: [");
pw.print(mLastResetTime);
pw.print("] ");
- pw.print(s.formatTime(mLastResetTime));
+ pw.print(ShortcutService.formatTime(mLastResetTime));
pw.println();
- getPackageInfo().dump(s, pw, prefix + " ");
+ getPackageInfo().dump(pw, prefix + " ");
pw.println();
pw.print(prefix);
@@ -545,11 +1205,47 @@
pw.print("Total bitmap size: ");
pw.print(totalBitmapSize);
pw.print(" (");
- pw.print(Formatter.formatFileSize(s.mContext, totalBitmapSize));
+ pw.print(Formatter.formatFileSize(mShortcutUser.mService.mContext, totalBitmapSize));
pw.println(")");
}
@Override
+ public JSONObject dumpCheckin(boolean clear) throws JSONException {
+ final JSONObject result = super.dumpCheckin(clear);
+
+ int numDynamic = 0;
+ int numPinned = 0;
+ int numManifest = 0;
+ int numBitmaps = 0;
+ long totalBitmapSize = 0;
+
+ final ArrayMap<String, ShortcutInfo> shortcuts = mShortcuts;
+ final int size = shortcuts.size();
+ for (int i = 0; i < size; i++) {
+ final ShortcutInfo si = shortcuts.valueAt(i);
+
+ if (si.isDynamic()) numDynamic++;
+ if (si.isDeclaredInManifest()) numManifest++;
+ if (si.isPinned()) numPinned++;
+
+ if (si.getBitmapPath() != null) {
+ numBitmaps++;
+ totalBitmapSize += new File(si.getBitmapPath()).length();
+ }
+ }
+
+ result.put(KEY_DYNAMIC, numDynamic);
+ result.put(KEY_MANIFEST, numManifest);
+ result.put(KEY_PINNED, numPinned);
+ result.put(KEY_BITMAPS, numBitmaps);
+ result.put(KEY_BITMAP_BYTES, totalBitmapSize);
+
+ // TODO Log update frequency too.
+
+ return result;
+ }
+
+ @Override
public void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
throws IOException, XmlPullParserException {
final int size = mShortcuts.size();
@@ -561,7 +1257,6 @@
out.startTag(null, TAG_ROOT);
ShortcutService.writeAttr(out, ATTR_NAME, getPackageName());
- ShortcutService.writeAttr(out, ATTR_DYNAMIC_COUNT, mDynamicShortcutCount);
ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
getPackageInfo().saveToXml(out);
@@ -576,19 +1271,26 @@
private static void saveShortcut(XmlSerializer out, ShortcutInfo si, boolean forBackup)
throws IOException, XmlPullParserException {
if (forBackup) {
- if (!si.isPinned()) {
- return; // Backup only pinned icons.
+ if (!(si.isPinned() && si.isEnabled())) {
+ return; // We only backup pinned shortcuts that are enabled.
}
}
out.startTag(null, TAG_SHORTCUT);
ShortcutService.writeAttr(out, ATTR_ID, si.getId());
// writeAttr(out, "package", si.getPackageName()); // not needed
- ShortcutService.writeAttr(out, ATTR_ACTIVITY, si.getActivityComponent());
+ ShortcutService.writeAttr(out, ATTR_ACTIVITY, si.getActivity());
// writeAttr(out, "icon", si.getIcon()); // We don't save it.
ShortcutService.writeAttr(out, ATTR_TITLE, si.getTitle());
+ ShortcutService.writeAttr(out, ATTR_TITLE_RES_ID, si.getTitleResId());
+ ShortcutService.writeAttr(out, ATTR_TITLE_RES_NAME, si.getTitleResName());
ShortcutService.writeAttr(out, ATTR_TEXT, si.getText());
- ShortcutService.writeAttr(out, ATTR_INTENT, si.getIntentNoExtras());
- ShortcutService.writeAttr(out, ATTR_WEIGHT, si.getWeight());
+ ShortcutService.writeAttr(out, ATTR_TEXT_RES_ID, si.getTextResId());
+ ShortcutService.writeAttr(out, ATTR_TEXT_RES_NAME, si.getTextResName());
+ ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE, si.getDisabledMessage());
+ ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE_RES_ID,
+ si.getDisabledMessageResourceId());
+ ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE_RES_NAME,
+ si.getDisabledMessageResName());
ShortcutService.writeAttr(out, ATTR_TIMESTAMP,
si.getLastChangedTimestamp());
if (forBackup) {
@@ -598,8 +1300,13 @@
~(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES
| ShortcutInfo.FLAG_DYNAMIC));
} else {
+ // When writing for backup, ranks shouldn't be saved, since shortcuts won't be restored
+ // as dynamic.
+ ShortcutService.writeAttr(out, ATTR_RANK, si.getRank());
+
ShortcutService.writeAttr(out, ATTR_FLAGS, si.getFlags());
- ShortcutService.writeAttr(out, ATTR_ICON_RES, si.getIconResourceId());
+ ShortcutService.writeAttr(out, ATTR_ICON_RES_ID, si.getIconResourceId());
+ ShortcutService.writeAttr(out, ATTR_ICON_RES_NAME, si.getIconResName());
ShortcutService.writeAttr(out, ATTR_BITMAP_PATH, si.getBitmapPath());
}
@@ -612,9 +1319,16 @@
out.endTag(null, TAG_CATEGORIES);
}
}
+ final Intent[] intentsNoExtras = si.getIntentsNoExtras();
+ final PersistableBundle[] intentsExtras = si.getIntentPersistableExtrases();
+ final int numIntents = intentsNoExtras.length;
+ for (int i = 0; i < numIntents; i++) {
+ out.startTag(null, TAG_INTENT);
+ ShortcutService.writeAttr(out, ATTR_INTENT_NO_EXTRA, intentsNoExtras[i]);
+ ShortcutService.writeTagExtra(out, TAG_EXTRAS, intentsExtras[i]);
+ out.endTag(null, TAG_INTENT);
+ }
- ShortcutService.writeTagExtra(out, TAG_INTENT_EXTRAS,
- si.getIntentPersistableExtras());
ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras());
out.endTag(null, TAG_SHORTCUT);
@@ -627,11 +1341,9 @@
final String packageName = ShortcutService.parseStringAttribute(parser,
ATTR_NAME);
- final ShortcutPackage ret = new ShortcutPackage(s, shortcutUser,
+ final ShortcutPackage ret = new ShortcutPackage(shortcutUser,
shortcutUser.getUserId(), packageName);
- ret.mDynamicShortcutCount =
- ShortcutService.parseIntAttribute(parser, ATTR_DYNAMIC_COUNT);
ret.mApiCallCount =
ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
ret.mLastResetTime =
@@ -671,14 +1383,23 @@
ComponentName activityComponent;
// Icon icon;
String title;
+ int titleResId;
+ String titleResName;
String text;
- Intent intent;
- PersistableBundle intentPersistableExtras = null;
- int weight;
+ int textResId;
+ String textResName;
+ String disabledMessage;
+ int disabledMessageResId;
+ String disabledMessageResName;
+ Intent intentLegacy;
+ PersistableBundle intentPersistableExtrasLegacy = null;
+ ArrayList<Intent> intents = new ArrayList<>();
+ int rank;
PersistableBundle extras = null;
long lastChangedTimestamp;
int flags;
- int iconRes;
+ int iconResId;
+ String iconResName;
String bitmapPath;
ArraySet<String> categories = null;
@@ -686,12 +1407,22 @@
activityComponent = ShortcutService.parseComponentNameAttribute(parser,
ATTR_ACTIVITY);
title = ShortcutService.parseStringAttribute(parser, ATTR_TITLE);
+ titleResId = ShortcutService.parseIntAttribute(parser, ATTR_TITLE_RES_ID);
+ titleResName = ShortcutService.parseStringAttribute(parser, ATTR_TITLE_RES_NAME);
text = ShortcutService.parseStringAttribute(parser, ATTR_TEXT);
- intent = ShortcutService.parseIntentAttribute(parser, ATTR_INTENT);
- weight = (int) ShortcutService.parseLongAttribute(parser, ATTR_WEIGHT);
+ textResId = ShortcutService.parseIntAttribute(parser, ATTR_TEXT_RES_ID);
+ textResName = ShortcutService.parseStringAttribute(parser, ATTR_TEXT_RES_NAME);
+ disabledMessage = ShortcutService.parseStringAttribute(parser, ATTR_DISABLED_MESSAGE);
+ disabledMessageResId = ShortcutService.parseIntAttribute(parser,
+ ATTR_DISABLED_MESSAGE_RES_ID);
+ disabledMessageResName = ShortcutService.parseStringAttribute(parser,
+ ATTR_DISABLED_MESSAGE_RES_NAME);
+ intentLegacy = ShortcutService.parseIntentAttributeNoDefault(parser, ATTR_INTENT_LEGACY);
+ rank = (int) ShortcutService.parseLongAttribute(parser, ATTR_RANK);
lastChangedTimestamp = ShortcutService.parseLongAttribute(parser, ATTR_TIMESTAMP);
flags = (int) ShortcutService.parseLongAttribute(parser, ATTR_FLAGS);
- iconRes = (int) ShortcutService.parseLongAttribute(parser, ATTR_ICON_RES);
+ iconResId = (int) ShortcutService.parseLongAttribute(parser, ATTR_ICON_RES_ID);
+ iconResName = ShortcutService.parseStringAttribute(parser, ATTR_ICON_RES_NAME);
bitmapPath = ShortcutService.parseStringAttribute(parser, ATTR_BITMAP_PATH);
final int outerDepth = parser.getDepth();
@@ -708,8 +1439,11 @@
depth, type, tag));
}
switch (tag) {
- case TAG_INTENT_EXTRAS:
- intentPersistableExtras = PersistableBundle.restoreFromXml(parser);
+ case TAG_INTENT_EXTRAS_LEGACY:
+ intentPersistableExtrasLegacy = PersistableBundle.restoreFromXml(parser);
+ continue;
+ case TAG_INTENT:
+ intents.add(parseIntent(parser));
continue;
case TAG_EXTRAS:
extras = PersistableBundle.restoreFromXml(parser);
@@ -732,15 +1466,146 @@
throw ShortcutService.throwForInvalidTag(depth, tag);
}
+ if (intentLegacy != null) {
+ // For the legacy file format which supported only one intent per shortcut.
+ ShortcutInfo.setIntentExtras(intentLegacy, intentPersistableExtrasLegacy);
+ intents.clear();
+ intents.add(intentLegacy);
+ }
+
return new ShortcutInfo(
- userId, id, packageName, activityComponent, /* icon =*/ null, title, text,
- categories, intent,
- intentPersistableExtras, weight, extras, lastChangedTimestamp, flags,
- iconRes, bitmapPath);
+ userId, id, packageName, activityComponent, /* icon =*/ null,
+ title, titleResId, titleResName, text, textResId, textResName,
+ disabledMessage, disabledMessageResId, disabledMessageResName,
+ categories,
+ intents.toArray(new Intent[intents.size()]),
+ rank, extras, lastChangedTimestamp, flags,
+ iconResId, iconResName, bitmapPath);
+ }
+
+ private static Intent parseIntent(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+
+ Intent intent = ShortcutService.parseIntentAttribute(parser,
+ ATTR_INTENT_NO_EXTRA);
+
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ final int depth = parser.getDepth();
+ final String tag = parser.getName();
+ if (ShortcutService.DEBUG_LOAD) {
+ Slog.d(TAG, String.format(" depth=%d type=%d name=%s",
+ depth, type, tag));
+ }
+ switch (tag) {
+ case TAG_EXTRAS:
+ ShortcutInfo.setIntentExtras(intent,
+ PersistableBundle.restoreFromXml(parser));
+ continue;
+ }
+ throw ShortcutService.throwForInvalidTag(depth, tag);
+ }
+ return intent;
}
@VisibleForTesting
List<ShortcutInfo> getAllShortcutsForTest() {
return new ArrayList<>(mShortcuts.values());
}
+
+ @Override
+ public void verifyStates() {
+ super.verifyStates();
+
+ boolean failed = false;
+
+ final ArrayMap<ComponentName, ArrayList<ShortcutInfo>> all =
+ sortShortcutsToActivities();
+
+ // Make sure each activity won't have more than max shortcuts.
+ for (int outer = all.size() - 1; outer >= 0; outer--) {
+ final ArrayList<ShortcutInfo> list = all.valueAt(outer);
+ if (list.size() > mShortcutUser.mService.getMaxActivityShortcuts()) {
+ failed = true;
+ Log.e(TAG_VERIFY, "Package " + getPackageName() + ": activity " + all.keyAt(outer)
+ + " has " + all.valueAt(outer).size() + " shortcuts.");
+ }
+
+ // Sort by rank.
+ Collections.sort(list, (a, b) -> Integer.compare(a.getRank(), b.getRank()));
+
+ // Split into two arrays for each kind.
+ final ArrayList<ShortcutInfo> dynamicList = new ArrayList<>(list);
+ dynamicList.removeIf((si) -> !si.isDynamic());
+
+ final ArrayList<ShortcutInfo> manifestList = new ArrayList<>(list);
+ dynamicList.removeIf((si) -> !si.isManifestShortcut());
+
+ verifyRanksSequential(dynamicList);
+ verifyRanksSequential(manifestList);
+ }
+
+ // Verify each shortcut's status.
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ final ShortcutInfo si = mShortcuts.valueAt(i);
+ if (!(si.isDeclaredInManifest() || si.isDynamic() || si.isPinned())) {
+ failed = true;
+ Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+ + " is not manifest, dynamic or pinned.");
+ }
+ if (si.isDeclaredInManifest() && si.isDynamic()) {
+ failed = true;
+ Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+ + " is both dynamic and manifest at the same time.");
+ }
+ if (si.getActivity() == null) {
+ failed = true;
+ Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+ + " has null activity.");
+ }
+ if ((si.isDynamic() || si.isManifestShortcut()) && !si.isEnabled()) {
+ failed = true;
+ Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+ + " is not floating, but is disabled.");
+ }
+ if (si.isFloating() && si.getRank() != 0) {
+ failed = true;
+ Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+ + " is floating, but has rank=" + si.getRank());
+ }
+ if (si.getIcon() != null) {
+ failed = true;
+ Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+ + " still has an icon");
+ }
+ if (si.hasIconFile() && si.hasIconResource()) {
+ failed = true;
+ Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+ + " has both resource and bitmap icons");
+ }
+ }
+
+ if (failed) {
+ throw new IllegalStateException("See logcat for errors");
+ }
+ }
+
+ private boolean verifyRanksSequential(List<ShortcutInfo> list) {
+ boolean failed = false;
+
+ for (int i = 0; i < list.size(); i++) {
+ final ShortcutInfo si = list.get(i);
+ if (si.getRank() != i) {
+ failed = true;
+ Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+ + " rank=" + si.getRank() + " but expected to be "+ i);
+ }
+ }
+ return failed;
+ }
}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
index 74969f0..e7b66fc 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
@@ -15,6 +15,7 @@
*/
package com.android.server.pm;
+import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.pm.PackageInfo;
import android.util.Slog;
@@ -34,12 +35,15 @@
/**
* Package information used by {@link android.content.pm.ShortcutManager} for backup / restore.
+ *
+ * All methods should be guarded by {@code ShortcutService.mLock}.
*/
class ShortcutPackageInfo {
private static final String TAG = ShortcutService.TAG;
static final String TAG_ROOT = "package-info";
private static final String ATTR_VERSION = "version";
+ private static final String ATTR_LAST_UPDATE_TIME = "last_udpate_time";
private static final String ATTR_SHADOW = "shadow";
private static final String TAG_SIGNATURE = "signature";
@@ -53,16 +57,20 @@
*/
private boolean mIsShadow;
private int mVersionCode = VERSION_UNKNOWN;
+ private long mLastUpdateTime;
private ArrayList<byte[]> mSigHashes;
- private ShortcutPackageInfo(int versionCode, ArrayList<byte[]> sigHashes, boolean isShadow) {
+ private ShortcutPackageInfo(int versionCode, long lastUpdateTime,
+ ArrayList<byte[]> sigHashes, boolean isShadow) {
mVersionCode = versionCode;
+ mLastUpdateTime = lastUpdateTime;
mIsShadow = isShadow;
mSigHashes = sigHashes;
}
public static ShortcutPackageInfo newEmpty() {
- return new ShortcutPackageInfo(VERSION_UNKNOWN, new ArrayList<>(0), /* isShadow */ false);
+ return new ShortcutPackageInfo(VERSION_UNKNOWN, /* last update time =*/ 0,
+ new ArrayList<>(0), /* isShadow */ false);
}
public boolean isShadow() {
@@ -77,8 +85,15 @@
return mVersionCode;
}
- public void setVersionCode(int versionCode) {
- mVersionCode = versionCode;
+ public long getLastUpdateTime() {
+ return mLastUpdateTime;
+ }
+
+ public void updateVersionInfo(@NonNull PackageInfo pi) {
+ if (pi != null) {
+ mVersionCode = pi.versionCode;
+ mLastUpdateTime = pi.lastUpdateTime;
+ }
}
public boolean hasSignatures() {
@@ -111,7 +126,7 @@
Slog.e(TAG, "Can't get signatures: package=" + packageName);
return null;
}
- final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.versionCode,
+ final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.versionCode, pi.lastUpdateTime,
BackupUtils.hashSignatureArray(pi.signatures), /* shadow=*/ false);
return ret;
@@ -131,6 +146,7 @@
return;
}
mVersionCode = pi.versionCode;
+ mLastUpdateTime = pi.lastUpdateTime;
mSigHashes = BackupUtils.hashSignatureArray(pi.signatures);
}
@@ -139,6 +155,7 @@
out.startTag(null, TAG_ROOT);
ShortcutService.writeAttr(out, ATTR_VERSION, mVersionCode);
+ ShortcutService.writeAttr(out, ATTR_LAST_UPDATE_TIME, mLastUpdateTime);
ShortcutService.writeAttr(out, ATTR_SHADOW, mIsShadow);
for (int i = 0; i < mSigHashes.size(); i++) {
@@ -154,6 +171,9 @@
final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION);
+ final long lastUpdateTime = ShortcutService.parseLongAttribute(
+ parser, ATTR_LAST_UPDATE_TIME);
+
// When restoring from backup, it's always shadow.
final boolean shadow =
fromBackup || ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW);
@@ -185,11 +205,12 @@
// Successfully loaded; replace the feilds.
mVersionCode = versionCode;
+ mLastUpdateTime = lastUpdateTime;
mIsShadow = shadow;
mSigHashes = hashes;
}
- public void dump(ShortcutService s, PrintWriter pw, String prefix) {
+ public void dump(PrintWriter pw, String prefix) {
pw.println();
pw.print(prefix);
@@ -205,6 +226,11 @@
pw.print(mVersionCode);
pw.println();
+ pw.print(prefix);
+ pw.print(" Last package update time: ");
+ pw.print(mLastUpdateTime);
+ pw.println();
+
for (int i = 0; i < mSigHashes.size(); i++) {
pw.print(prefix);
pw.print(" ");
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index 6fbdb82..79b5c4e 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -21,13 +21,19 @@
import com.android.internal.util.Preconditions;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
+/**
+ * All methods should be guarded by {@code #mShortcutUser.mService.mLock}.
+ */
abstract class ShortcutPackageItem {
private static final String TAG = ShortcutService.TAG;
+ private static final String KEY_NAME = "name";
private final int mPackageUserId;
private final String mPackageName;
@@ -45,6 +51,10 @@
mPackageInfo = Preconditions.checkNotNull(packageInfo);
}
+ public ShortcutUser getUser() {
+ return mShortcutUser;
+ }
+
/**
* ID of the user who actually has this package running on. For {@link ShortcutPackage},
* this is the same thing as {@link #getOwnerUserId}, but if it's a {@link ShortcutLauncher} and
@@ -69,18 +79,20 @@
return mPackageInfo;
}
- public void refreshPackageInfoAndSave(ShortcutService s) {
+ public void refreshPackageInfoAndSave() {
if (mPackageInfo.isShadow()) {
return; // Don't refresh for shadow user.
}
+ final ShortcutService s = mShortcutUser.mService;
mPackageInfo.refresh(s, this);
s.scheduleSaveUser(getOwnerUserId());
}
- public void attemptToRestoreIfNeededAndSave(ShortcutService s) {
+ public void attemptToRestoreIfNeededAndSave() {
if (!mPackageInfo.isShadow()) {
return; // Already installed, nothing to do.
}
+ final ShortcutService s = mShortcutUser.mService;
if (!s.isPackageInstalled(mPackageName, mPackageUserId)) {
if (ShortcutService.DEBUG) {
Slog.d(TAG, String.format("Package still not installed: %s user=%d",
@@ -91,14 +103,14 @@
if (!mPackageInfo.hasSignatures()) {
s.wtf("Attempted to restore package " + mPackageName + ", user=" + mPackageUserId
+ " but signatures not found in the restore data.");
- onRestoreBlocked(s);
+ onRestoreBlocked();
return;
}
final PackageInfo pi = s.getPackageInfoWithSignatures(mPackageName, mPackageUserId);
if (!mPackageInfo.canRestoreTo(s, pi)) {
// Package is now installed, but can't restore. Let the subclass do the cleanup.
- onRestoreBlocked(s);
+ onRestoreBlocked();
return;
}
if (ShortcutService.DEBUG) {
@@ -106,7 +118,7 @@
mPackageUserId, getOwnerUserId()));
}
- onRestored(s);
+ onRestored();
// Now the package is not shadow.
mPackageInfo.setShadow(false);
@@ -118,13 +130,25 @@
* Called when the new package can't be restored because it has a lower version number
* or different signatures.
*/
- protected abstract void onRestoreBlocked(ShortcutService s);
+ protected abstract void onRestoreBlocked();
/**
* Called when the new package is successfully restored.
*/
- protected abstract void onRestored(ShortcutService s);
+ protected abstract void onRestored();
public abstract void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
throws IOException, XmlPullParserException;
+
+ public JSONObject dumpCheckin(boolean clear) throws JSONException {
+ final JSONObject result = new JSONObject();
+ result.put(KEY_NAME, mPackageName);
+ return result;
+ }
+
+ /**
+ * Verify various internal states.
+ */
+ public void verifyStates() {
+ }
}
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
new file mode 100644
index 0000000..3cf4200
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.util.Xml;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class ShortcutParser {
+ private static final String TAG = ShortcutService.TAG;
+
+ private static final boolean DEBUG = ShortcutService.DEBUG || false; // DO NOT SUBMIT WITH TRUE
+
+ @VisibleForTesting
+ static final String METADATA_KEY = "android.app.shortcuts";
+
+ private static final String TAG_SHORTCUTS = "shortcuts";
+ private static final String TAG_SHORTCUT = "shortcut";
+ private static final String TAG_INTENT = "intent";
+ private static final String TAG_CATEGORIES = "categories";
+
+ @Nullable
+ public static List<ShortcutInfo> parseShortcuts(ShortcutService service,
+ String packageName, @UserIdInt int userId) throws IOException, XmlPullParserException {
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, String.format("Scanning package %s for manifest shortcuts on user %d",
+ packageName, userId));
+ }
+ final List<ResolveInfo> activities = service.injectGetMainActivities(packageName, userId);
+ if (activities == null || activities.size() == 0) {
+ return null;
+ }
+
+ List<ShortcutInfo> result = null;
+
+ try {
+ final int size = activities.size();
+ for (int i = 0; i < size; i++) {
+ final ActivityInfo activityInfoNoMetadata = activities.get(i).activityInfo;
+ if (activityInfoNoMetadata == null) {
+ continue;
+ }
+
+ final ActivityInfo activityInfoWithMetadata =
+ service.getActivityInfoWithMetadata(
+ activityInfoNoMetadata.getComponentName(), userId);
+ if (activityInfoWithMetadata != null) {
+ result = parseShortcutsOneFile(
+ service, activityInfoWithMetadata, packageName, userId, result);
+ }
+ }
+ } catch (RuntimeException e) {
+ // Resource ID mismatch may cause various runtime exceptions when parsing XMLs,
+ // But we don't crash the device, so just swallow them.
+ service.wtf(
+ "Exception caught while parsing shortcut XML for package=" + packageName, e);
+ return null;
+ }
+ return result;
+ }
+
+ private static List<ShortcutInfo> parseShortcutsOneFile(
+ ShortcutService service,
+ ActivityInfo activityInfo, String packageName, @UserIdInt int userId,
+ List<ShortcutInfo> result) throws IOException, XmlPullParserException {
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, String.format(
+ "Checking main activity %s", activityInfo.getComponentName()));
+ }
+
+ XmlResourceParser parser = null;
+ try {
+ parser = service.injectXmlMetaData(activityInfo, METADATA_KEY);
+ if (parser == null) {
+ return result;
+ }
+
+ final ComponentName activity = new ComponentName(packageName, activityInfo.name);
+
+ final AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+
+ int rank = 0;
+ final int maxShortcuts = service.getMaxActivityShortcuts();
+ int numShortcuts = 0;
+
+ // We instantiate ShortcutInfo at <shortcut>, but we add it to the list at </shortcut>,
+ // after parsing <intent>. We keep the current one in here.
+ ShortcutInfo currentShortcut = null;
+
+ Set<String> categories = null;
+ final ArrayList<Intent> intents = new ArrayList<>();
+
+ outer:
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > 0)) {
+ final int depth = parser.getDepth();
+ final String tag = parser.getName();
+
+ // When a shortcut tag is closing, publish.
+ if ((type == XmlPullParser.END_TAG) && (depth == 2) && (TAG_SHORTCUT.equals(tag))) {
+ if (currentShortcut == null) {
+ // Shortcut was invalid.
+ continue;
+ }
+ final ShortcutInfo si = currentShortcut;
+ currentShortcut = null; // Make sure to null out for the next iteration.
+
+ if (si.isEnabled()) {
+ if (intents.size() == 0) {
+ Log.e(TAG, "Shortcut " + si.getId() + " has no intent. Skipping it.");
+ continue;
+ }
+ } else {
+ // Just set the default intent to disabled shortcuts.
+ intents.clear();
+ intents.add(new Intent(Intent.ACTION_VIEW));
+ }
+
+ if (numShortcuts >= maxShortcuts) {
+ Log.e(TAG, "More than " + maxShortcuts + " shortcuts found for "
+ + activityInfo.getComponentName() + ". Skipping the rest.");
+ return result;
+ }
+
+ // Same flag as what TaskStackBuilder adds.
+ intents.get(0).addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_CLEAR_TASK |
+ Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+ try {
+ si.setIntents(intents.toArray(new Intent[intents.size()]));
+ } catch (RuntimeException e) {
+ // This shouldn't happen because intents in XML can't have complicated
+ // extras, but just in case Intent.parseIntent() supports such a thing one
+ // day.
+ Log.e(TAG, "Shortcut's extras contain un-persistable values. Skipping it.");
+ continue;
+ }
+ intents.clear();
+
+ if (categories != null) {
+ si.setCategories(categories);
+ categories = null;
+ }
+
+ if (result == null) {
+ result = new ArrayList<>();
+ }
+ result.add(si);
+ numShortcuts++;
+ rank++;
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, "Shortcut added: " + si.toInsecureString());
+ }
+ continue;
+ }
+
+ // Otherwise, just look at start tags.
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ if (depth == 1 && TAG_SHORTCUTS.equals(tag)) {
+ continue; // Root tag.
+ }
+ if (depth == 2 && TAG_SHORTCUT.equals(tag)) {
+ final ShortcutInfo si = parseShortcutAttributes(
+ service, attrs, packageName, activity, userId, rank);
+ if (si == null) {
+ // Shortcut was invalid.
+ continue;
+ }
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, "Shortcut found: " + si.toInsecureString());
+ }
+ if (result != null) {
+ for (int i = result.size() - 1; i >= 0; i--) {
+ if (si.getId().equals(result.get(i).getId())) {
+ Log.e(TAG, "Duplicate shortcut ID detected. Skipping it.");
+ continue outer;
+ }
+ }
+ }
+ currentShortcut = si;
+ categories = null;
+ continue;
+ }
+ if (depth == 3 && TAG_INTENT.equals(tag)) {
+ if ((currentShortcut == null)
+ || !currentShortcut.isEnabled()) {
+ Log.e(TAG, "Ignoring excessive intent tag.");
+ continue;
+ }
+
+ final Intent intent = Intent.parseIntent(service.mContext.getResources(),
+ parser, attrs);
+ if (TextUtils.isEmpty(intent.getAction())) {
+ Log.e(TAG, "Shortcut intent action must be provided. activity=" + activity);
+ currentShortcut = null; // Invalidate the current shortcut.
+ continue;
+ }
+ intents.add(intent);
+ continue;
+ }
+ if (depth == 3 && TAG_CATEGORIES.equals(tag)) {
+ if ((currentShortcut == null)
+ || (currentShortcut.getCategories() != null)) {
+ continue;
+ }
+ final String name = parseCategories(service, attrs);
+ if (TextUtils.isEmpty(name)) {
+ Log.e(TAG, "Empty category found. activity=" + activity);
+ continue;
+ }
+
+ if (categories == null) {
+ categories = new ArraySet<>();
+ }
+ categories.add(name);
+ continue;
+ }
+
+ Log.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth));
+ }
+ } finally {
+ if (parser != null) {
+ parser.close();
+ }
+ }
+ return result;
+ }
+
+ private static String parseCategories(ShortcutService service, AttributeSet attrs) {
+ final TypedArray sa = service.mContext.getResources().obtainAttributes(attrs,
+ R.styleable.ShortcutCategories);
+ try {
+ if (sa.getType(R.styleable.ShortcutCategories_name) == TypedValue.TYPE_STRING) {
+ return sa.getNonResourceString(R.styleable.ShortcutCategories_name);
+ } else {
+ Log.w(TAG, "android:name for shortcut category must be string literal.");
+ return null;
+ }
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static ShortcutInfo parseShortcutAttributes(ShortcutService service,
+ AttributeSet attrs, String packageName, ComponentName activity,
+ @UserIdInt int userId, int rank) {
+ final TypedArray sa = service.mContext.getResources().obtainAttributes(attrs,
+ R.styleable.Shortcut);
+ try {
+ if (sa.getType(R.styleable.Shortcut_shortcutId) != TypedValue.TYPE_STRING) {
+ Log.w(TAG, "android:shortcutId must be string literal. activity=" + activity);
+ return null;
+ }
+ final String id = sa.getNonResourceString(R.styleable.Shortcut_shortcutId);
+ final boolean enabled = sa.getBoolean(R.styleable.Shortcut_enabled, true);
+ final int iconResId = sa.getResourceId(R.styleable.Shortcut_icon, 0);
+ final int titleResId = sa.getResourceId(R.styleable.Shortcut_shortcutShortLabel, 0);
+ final int textResId = sa.getResourceId(R.styleable.Shortcut_shortcutLongLabel, 0);
+ final int disabledMessageResId = sa.getResourceId(
+ R.styleable.Shortcut_shortcutDisabledMessage, 0);
+
+ if (TextUtils.isEmpty(id)) {
+ Log.w(TAG, "android:shortcutId must be provided. activity=" + activity);
+ return null;
+ }
+ if (titleResId == 0) {
+ Log.w(TAG, "android:shortcutShortLabel must be provided. activity=" + activity);
+ return null;
+ }
+
+ return createShortcutFromManifest(
+ service,
+ userId,
+ id,
+ packageName,
+ activity,
+ titleResId,
+ textResId,
+ disabledMessageResId,
+ rank,
+ iconResId,
+ enabled);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static ShortcutInfo createShortcutFromManifest(ShortcutService service,
+ @UserIdInt int userId, String id, String packageName, ComponentName activityComponent,
+ int titleResId, int textResId, int disabledMessageResId,
+ int rank, int iconResId, boolean enabled) {
+
+ final int flags =
+ (enabled ? ShortcutInfo.FLAG_MANIFEST : ShortcutInfo.FLAG_DISABLED)
+ | ShortcutInfo.FLAG_IMMUTABLE
+ | ((iconResId != 0) ? ShortcutInfo.FLAG_HAS_ICON_RES : 0);
+
+ // Note we don't need to set resource names here yet. They'll be set when they're about
+ // to be published.
+ return new ShortcutInfo(
+ userId,
+ id,
+ packageName,
+ activityComponent,
+ null, // icon
+ null, // title string
+ titleResId,
+ null, // title res name
+ null, // text string
+ textResId,
+ null, // text res name
+ null, // disabled message string
+ disabledMessageResId,
+ null, // disabled message res name
+ null, // categories
+ null, // intent
+ rank,
+ null, // extras
+ service.injectCurrentTimeMillis(),
+ flags,
+ iconResId,
+ null, // icon res name
+ null); // bitmap path
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 8268776..c1fc7f1 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -15,16 +15,22 @@
*/
package com.android.server.pm;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
import android.app.IUidObserver;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.IShortcutService;
@@ -32,21 +38,26 @@
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.Canvas;
import android.graphics.RectF;
import android.graphics.drawable.Icon;
+import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
+import android.os.LocaleList;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
@@ -54,6 +65,7 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SELinux;
+import android.os.ServiceManager;
import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -63,18 +75,19 @@
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.KeyValueListParser;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.util.TypedValue;
import android.util.Xml;
+import android.view.IWindowManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
@@ -83,6 +96,9 @@
import libcore.io.IoUtils;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -100,33 +116,28 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
-import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* TODO:
- *
- * - Default launcher check does take a few ms. Worth caching.
- *
- * - Clear data -> remove all dynamic? but not the pinned?
- *
- * - Scan and remove orphan bitmaps (just in case).
+ * - getIconMaxWidth()/getIconMaxHeight() should use xdpi and ydpi.
+ * -> But TypedValue.applyDimension() doesn't differentiate x and y..?
*
* - Detect when already registered instances are passed to APIs again, which might break
- * internal bitmap handling.
- *
- * - Add more call stats.
+ * internal bitmap handling.
*/
public class ShortcutService extends IShortcutService.Stub {
static final String TAG = "ShortcutService";
- public static final boolean FEATURE_ENABLED = false;
-
static final boolean DEBUG = false; // STOPSHIP if true
static final boolean DEBUG_LOAD = false; // STOPSHIP if true
static final boolean DEBUG_PROCSTATE = false; // STOPSHIP if true
@@ -168,10 +179,15 @@
private static final String TAG_ROOT = "root";
private static final String TAG_LAST_RESET_TIME = "last_reset_time";
- private static final String TAG_LOCALE_CHANGE_SEQUENCE_NUMBER = "locale_seq_no";
private static final String ATTR_VALUE = "value";
+ private static final String LAUNCHER_INTENT_CATEGORY = Intent.CATEGORY_LAUNCHER;
+
+ private static final String KEY_SHORTCUT = "shortcut";
+ private static final String KEY_LOW_RAM = "lowRam";
+ private static final String KEY_ICON_SIZE = "iconSize";
+
@VisibleForTesting
interface ConfigConstants {
/**
@@ -200,7 +216,7 @@
String KEY_MAX_ICON_DIMENSION_DP_LOWRAM = "max_icon_dimension_dp_lowram";
/**
- * Key name for the max dynamic shortcuts per app. (int)
+ * Key name for the max dynamic shortcuts per activity. (int)
*/
String KEY_MAX_SHORTCUTS = "max_shortcuts";
@@ -219,6 +235,13 @@
private final Object mLock = new Object();
+ private static List<ResolveInfo> EMPTY_RESOLVE_INFO = new ArrayList<>(0);
+
+ private static Predicate<ResolveInfo> ACTIVITY_NOT_EXPORTED =
+ ri -> !ri.activityInfo.exported;
+
+ private static Predicate<PackageInfo> PACKAGE_NOT_INSTALLED = pi -> !isInstalled(pi);
+
private final Handler mHandler;
@GuardedBy("mLock")
@@ -234,9 +257,9 @@
private final SparseArray<ShortcutUser> mUsers = new SparseArray<>();
/**
- * Max number of dynamic shortcuts that each application can have at a time.
+ * Max number of dynamic + manifest shortcuts that each application can have at a time.
*/
- private int mMaxDynamicShortcuts;
+ private int mMaxShortcuts;
/**
* Max number of updating API calls that each application can make during the interval.
@@ -261,6 +284,8 @@
private final IPackageManager mIPackageManager;
private final PackageManagerInternal mPackageManagerInternal;
private final UserManager mUserManager;
+ private final UsageStatsManagerInternal mUsageStatsManagerInternal;
+ private final ActivityManagerInternal mActivityManagerInternal;
@GuardedBy("mLock")
final SparseIntArray mUidState = new SparseIntArray();
@@ -271,19 +296,15 @@
@GuardedBy("mLock")
private List<Integer> mDirtyUserIds = new ArrayList<>();
- /**
- * A counter that increments every time the system locale changes. We keep track of it to reset
- * throttling counters on the first call from each package after the last locale change.
- *
- * We need this mechanism because we can't do much in the locale change callback, which is
- * {@link ShortcutServiceInternal#onSystemLocaleChangedNoLock()}.
- */
- private final AtomicLong mLocaleChangeSequenceNumber = new AtomicLong();
+ private final AtomicBoolean mBootCompleted = new AtomicBoolean();
private static final int PACKAGE_MATCH_FLAGS =
PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_UNINSTALLED_PACKAGES;
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES;
+
+ @GuardedBy("mLock")
+ final SparseBooleanArray mUnlockedUsers = new SparseBooleanArray();
// Stats
@VisibleForTesting
@@ -293,8 +314,18 @@
int GET_PACKAGE_INFO_WITH_SIG = 2;
int GET_APPLICATION_INFO = 3;
int LAUNCHER_PERMISSION_CHECK = 4;
+ int CLEANUP_DANGLING_BITMAPS = 5;
+ int GET_ACTIVITY_WITH_METADATA = 6;
+ int GET_INSTALLED_PACKAGES = 7;
+ int CHECK_PACKAGE_CHANGES = 8;
+ int GET_APPLICATION_RESOURCES = 9;
+ int RESOURCE_NAME_LOOKUP = 10;
+ int GET_LAUNCHER_ACTIVITY = 11;
+ int CHECK_LAUNCHER_ACTIVITY = 12;
+ int IS_ACTIVITY_ENABLED = 13;
+ int PACKAGE_UPDATE_CHECK = 14;
- int COUNT = LAUNCHER_PERMISSION_CHECK + 1;
+ int COUNT = PACKAGE_UPDATE_CHECK + 1;
}
final Object mStatLock = new Object();
@@ -308,23 +339,73 @@
private static final int PROCESS_STATE_FOREGROUND_THRESHOLD =
ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+ static final int OPERATION_SET = 0;
+ static final int OPERATION_ADD = 1;
+ static final int OPERATION_UPDATE = 2;
+
+ /** @hide */
+ @IntDef(value = {
+ OPERATION_SET,
+ OPERATION_ADD,
+ OPERATION_UPDATE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ShortcutOperation {
+ }
+
+ @GuardedBy("mLock")
+ private int mWtfCount = 0;
+
+ @GuardedBy("mLock")
+ private Exception mLastWtfStacktrace;
+
public ShortcutService(Context context) {
- this(context, BackgroundThread.get().getLooper());
+ this(context, BackgroundThread.get().getLooper(), /*onyForPackgeManagerApis*/ false);
}
@VisibleForTesting
- ShortcutService(Context context, Looper looper) {
+ ShortcutService(Context context, Looper looper, boolean onlyForPackageManagerApis) {
mContext = Preconditions.checkNotNull(context);
LocalServices.addService(ShortcutServiceInternal.class, new LocalService());
mHandler = new Handler(looper);
mIPackageManager = AppGlobals.getPackageManager();
- mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
- mUserManager = context.getSystemService(UserManager.class);
+ mPackageManagerInternal = Preconditions.checkNotNull(
+ LocalServices.getService(PackageManagerInternal.class));
+ mUserManager = Preconditions.checkNotNull(context.getSystemService(UserManager.class));
+ mUsageStatsManagerInternal = Preconditions.checkNotNull(
+ LocalServices.getService(UsageStatsManagerInternal.class));
+ mActivityManagerInternal = Preconditions.checkNotNull(
+ LocalServices.getService(ActivityManagerInternal.class));
- if (!FEATURE_ENABLED) {
- return;
+ if (onlyForPackageManagerApis) {
+ return; // Don't do anything further. For unit tests only.
}
- mPackageMonitor.register(context, looper, UserHandle.ALL, /* externalStorage= */ false);
+
+ // Register receivers.
+
+ // We need to set a priority, so let's just not use PackageMonitor for now.
+ // TODO Refactor PackageMonitor to support priorities.
+ final IntentFilter packageFilter = new IntentFilter();
+ packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
+ packageFilter.addDataScheme("package");
+ packageFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ mContext.registerReceiverAsUser(mPackageMonitor, UserHandle.ALL,
+ packageFilter, null, mHandler);
+
+ final IntentFilter preferedActivityFilter = new IntentFilter();
+ preferedActivityFilter.addAction(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED);
+ preferedActivityFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ mContext.registerReceiverAsUser(mPackageMonitor, UserHandle.ALL,
+ preferedActivityFilter, null, mHandler);
+
+ final IntentFilter localeFilter = new IntentFilter();
+ localeFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
+ localeFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL,
+ localeFilter, null, mHandler);
injectRegisterUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE
| ActivityManager.UID_OBSERVER_GONE);
@@ -333,27 +414,32 @@
void logDurationStat(int statId, long start) {
synchronized (mStatLock) {
mCountStats[statId]++;
- mDurationStats[statId] += (System.currentTimeMillis() - start);
+ mDurationStats[statId] += (injectElapsedRealtime() - start);
}
}
- public long getLocaleChangeSequenceNumber() {
- return mLocaleChangeSequenceNumber.get();
+ public String injectGetLocaleTagsForUser(@UserIdInt int userId) {
+ // TODO This should get the per-user locale. b/30123329 b/30119489
+ return LocaleList.getDefault().toLanguageTags();
}
final private IUidObserver mUidObserver = new IUidObserver.Stub() {
- @Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
+ @Override
+ public void onUidStateChanged(int uid, int procState) throws RemoteException {
handleOnUidStateChanged(uid, procState);
}
- @Override public void onUidGone(int uid) throws RemoteException {
+ @Override
+ public void onUidGone(int uid) throws RemoteException {
handleOnUidStateChanged(uid, ActivityManager.MAX_PROCESS_STATE);
}
- @Override public void onUidActive(int uid) throws RemoteException {
+ @Override
+ public void onUidActive(int uid) throws RemoteException {
}
- @Override public void onUidIdle(int uid) throws RemoteException {
+ @Override
+ public void onUidIdle(int uid) throws RemoteException {
}
};
@@ -374,7 +460,8 @@
}
private boolean isProcessStateForeground(int processState) {
- return processState <= PROCESS_STATE_FOREGROUND_THRESHOLD;
+ return (processState != ActivityManager.PROCESS_STATE_NONEXISTENT)
+ && (processState <= PROCESS_STATE_FOREGROUND_THRESHOLD);
}
boolean isUidForegroundLocked(int uid) {
@@ -383,7 +470,13 @@
// so it's foreground anyway.
return true;
}
- return isProcessStateForeground(mUidState.get(uid, ActivityManager.MAX_PROCESS_STATE));
+ // First, check with the local cache.
+ if (isProcessStateForeground(mUidState.get(uid, ActivityManager.MAX_PROCESS_STATE))) {
+ return true;
+ }
+ // If the cache says background, reach out to AM. Since it'll internally need to hold
+ // the AM lock, we use it as a last resort.
+ return isProcessStateForeground(mActivityManagerInternal.getUidProcessState(uid));
}
long getUidLastForegroundElapsedTimeLocked(int uid) {
@@ -424,7 +517,6 @@
/** lifecycle event */
void onBootPhase(int phase) {
- // We want to call initialize() to initialize the configurations, so we don't disable this.
if (DEBUG) {
Slog.d(TAG, "onBootPhase: " + phase);
}
@@ -432,17 +524,25 @@
case SystemService.PHASE_LOCK_SETTINGS_READY:
initialize();
break;
+ case SystemService.PHASE_BOOT_COMPLETED:
+ mBootCompleted.set(true);
+ break;
}
}
/** lifecycle event */
void handleUnlockUser(int userId) {
- if (!FEATURE_ENABLED) {
- return;
+ if (DEBUG) {
+ Slog.d(TAG, "handleUnlockUser: user=" + userId);
}
synchronized (mLock) {
- // Preload
- getUserShortcutsLocked(userId);
+ mUnlockedUsers.put(userId, true);
+
+ // Preload the user's shortcuts.
+ // Also see if the locale has changed.
+ // Note as of nyc, the locale is per-user, so the locale shouldn't change
+ // when the user is locked. However due to b/30119489 it still happens.
+ getUserShortcutsLocked(userId).detectLocaleChange();
checkPackageChanges(userId);
}
@@ -450,11 +550,13 @@
/** lifecycle event */
void handleCleanupUser(int userId) {
- if (!FEATURE_ENABLED) {
- return;
+ if (DEBUG) {
+ Slog.d(TAG, "handleCleanupUser: user=" + userId);
}
synchronized (mLock) {
unloadUserLocked(userId);
+
+ mUnlockedUsers.put(userId, false);
}
}
@@ -520,16 +622,16 @@
mMaxUpdatesPerInterval = Math.max(0, (int) parser.getLong(
ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL, DEFAULT_MAX_UPDATES_PER_INTERVAL));
- mMaxDynamicShortcuts = Math.max(0, (int) parser.getLong(
+ mMaxShortcuts = Math.max(0, (int) parser.getLong(
ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_APP));
final int iconDimensionDp = Math.max(1, injectIsLowRamDevice()
? (int) parser.getLong(
- ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM,
- DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP)
+ ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM,
+ DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP)
: (int) parser.getLong(
- ConfigConstants.KEY_MAX_ICON_DIMENSION_DP,
- DEFAULT_MAX_ICON_DIMENSION_DP));
+ ConfigConstants.KEY_MAX_ICON_DIMENSION_DP,
+ DEFAULT_MAX_ICON_DIMENSION_DP));
mMaxIconDimension = injectDipToPixel(iconDimensionDp);
@@ -602,17 +704,27 @@
}
@Nullable
- static Intent parseIntentAttribute(XmlPullParser parser, String attribute) {
+ static Intent parseIntentAttributeNoDefault(XmlPullParser parser, String attribute) {
final String value = parseStringAttribute(parser, attribute);
- if (TextUtils.isEmpty(value)) {
- return null;
+ Intent parsed = null;
+ if (!TextUtils.isEmpty(value)) {
+ try {
+ parsed = Intent.parseUri(value, /* flags =*/ 0);
+ } catch (URISyntaxException e) {
+ Slog.e(TAG, "Error parsing intent", e);
+ }
}
- try {
- return Intent.parseUri(value, /* flags =*/ 0);
- } catch (URISyntaxException e) {
- Slog.e(TAG, "Error parsing intent", e);
- return null;
+ return parsed;
+ }
+
+ @Nullable
+ static Intent parseIntentAttribute(XmlPullParser parser, String attribute) {
+ Intent parsed = parseIntentAttributeNoDefault(parser, attribute);
+ if (parsed == null) {
+ // Default intent.
+ parsed = new Intent(Intent.ACTION_VIEW);
}
+ return parsed;
}
static void writeTagValue(XmlSerializer out, String tag, String value) throws IOException {
@@ -641,10 +753,10 @@
out.endTag(null, tag);
}
- static void writeAttr(XmlSerializer out, String name, String value) throws IOException {
+ static void writeAttr(XmlSerializer out, String name, CharSequence value) throws IOException {
if (TextUtils.isEmpty(value)) return;
- out.attribute(null, name, value);
+ out.attribute(null, name, value.toString());
}
static void writeAttr(XmlSerializer out, String name, long value) throws IOException {
@@ -687,8 +799,6 @@
// Body.
writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime);
- writeTagValue(out, TAG_LOCALE_CHANGE_SEQUENCE_NUMBER,
- mLocaleChangeSequenceNumber.get());
// Epilogue.
out.endTag(null, TAG_ROOT);
@@ -733,9 +843,6 @@
case TAG_LAST_RESET_TIME:
mRawLastResetTime = parseLongAttribute(parser, ATTR_VALUE);
break;
- case TAG_LOCALE_CHANGE_SEQUENCE_NUMBER:
- mLocaleChangeSequenceNumber.set(parseLongAttribute(parser, ATTR_VALUE));
- break;
default:
Slog.e(TAG, "Invalid tag: " + tag);
break;
@@ -743,7 +850,7 @@
}
} catch (FileNotFoundException e) {
// Use the default
- } catch (IOException|XmlPullParserException e) {
+ } catch (IOException | XmlPullParserException e) {
Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
mRawLastResetTime = 0;
@@ -752,12 +859,17 @@
getLastResetTimeLocked();
}
+ @VisibleForTesting
+ final File getUserFile(@UserIdInt int userId) {
+ return new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
+ }
+
private void saveUserLocked(@UserIdInt int userId) {
- final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
+ final File path = getUserFile(userId);
if (DEBUG) {
Slog.d(TAG, "Saving to " + path);
}
- path.mkdirs();
+ path.getParentFile().mkdirs();
final AtomicFile file = new AtomicFile(path);
FileOutputStream os = null;
try {
@@ -766,7 +878,10 @@
saveUserInternalLocked(userId, os, /* forBackup= */ false);
file.finishWrite(os);
- } catch (XmlPullParserException|IOException e) {
+
+ // Remove all dangling bitmap files.
+ cleanupDanglingBitmapDirectoriesLocked(userId);
+ } catch (XmlPullParserException | IOException e) {
Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
file.failWrite(os);
}
@@ -782,7 +897,7 @@
out.setOutput(bos, StandardCharsets.UTF_8.name());
out.startDocument(null, true);
- getUserShortcutsLocked(userId).saveToXml(this, out, forBackup);
+ getUserShortcutsLocked(userId).saveToXml(out, forBackup);
out.endDocument();
@@ -800,7 +915,7 @@
@Nullable
private ShortcutUser loadUserLocked(@UserIdInt int userId) {
- final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
+ final File path = getUserFile(userId);
if (DEBUG) {
Slog.d(TAG, "Loading from " + path);
}
@@ -816,8 +931,9 @@
return null;
}
try {
- return loadUserInternal(userId, in, /* forBackup= */ false);
- } catch (IOException|XmlPullParserException e) {
+ final ShortcutUser ret = loadUserInternal(userId, in, /* forBackup= */ false);
+ return ret;
+ } catch (IOException | XmlPullParserException e) {
Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
return null;
} finally {
@@ -885,16 +1001,20 @@
if (DEBUG) {
Slog.d(TAG, "saveDirtyInfo");
}
- synchronized (mLock) {
- for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) {
- final int userId = mDirtyUserIds.get(i);
- if (userId == UserHandle.USER_NULL) { // USER_NULL for base state.
- saveBaseStateLocked();
- } else {
- saveUserLocked(userId);
+ try {
+ synchronized (mLock) {
+ for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) {
+ final int userId = mDirtyUserIds.get(i);
+ if (userId == UserHandle.USER_NULL) { // USER_NULL for base state.
+ saveBaseStateLocked();
+ } else {
+ saveUserLocked(userId);
+ }
}
+ mDirtyUserIds.clear();
}
- mDirtyUserIds.clear();
+ } catch (Exception e) {
+ wtf("Exception in saveDirtyInfo", e);
}
}
@@ -944,6 +1064,31 @@
}
}
+ // Requires mLock held, but "Locked" prefix would look weired so we just say "L".
+ protected boolean isUserUnlockedL(@UserIdInt int userId) {
+ // First, check the local copy.
+ if (mUnlockedUsers.get(userId)) {
+ return true;
+ }
+ // If the local copy says the user is locked, check with AM for the actual state, since
+ // the user might just have been unlocked.
+ // Note we just don't use isUserUnlockingOrUnlocked() here, because it'll return false
+ // when the user is STOPPING, which we still want to consider as "unlocked".
+ final long token = injectClearCallingIdentity();
+ try {
+ return mUserManager.isUserUnlockingOrUnlocked(userId);
+ } finally {
+ injectRestoreCallingIdentity(token);
+ }
+ }
+
+ // Requires mLock held, but "Locked" prefix would look weired so we jsut say "L".
+ void throwIfUserLockedL(@UserIdInt int userId) {
+ if (!isUserUnlockedL(userId)) {
+ throw new IllegalStateException("User " + userId + " is locked or not running");
+ }
+ }
+
@GuardedBy("mLock")
@NonNull
private boolean isUserLoadedLocked(@UserIdInt int userId) {
@@ -954,11 +1099,15 @@
@GuardedBy("mLock")
@NonNull
ShortcutUser getUserShortcutsLocked(@UserIdInt int userId) {
+ if (!isUserUnlockedL(userId)) {
+ wtf("User still locked");
+ }
+
ShortcutUser userPackages = mUsers.get(userId);
if (userPackages == null) {
userPackages = loadUserLocked(userId);
if (userPackages == null) {
- userPackages = new ShortcutUser(userId);
+ userPackages = new ShortcutUser(this, userId);
}
mUsers.put(userId, userPackages);
}
@@ -976,7 +1125,7 @@
@NonNull
ShortcutPackage getPackageShortcutsLocked(
@NonNull String packageName, @UserIdInt int userId) {
- return getUserShortcutsLocked(userId).getPackageShortcuts(this, packageName);
+ return getUserShortcutsLocked(userId).getPackageShortcuts(packageName);
}
@GuardedBy("mLock")
@@ -985,22 +1134,17 @@
@NonNull String packageName, @UserIdInt int ownerUserId,
@UserIdInt int launcherUserId) {
return getUserShortcutsLocked(ownerUserId)
- .getLauncherShortcuts(this, packageName, launcherUserId);
+ .getLauncherShortcuts(packageName, launcherUserId);
}
// === Caller validation ===
void removeIcon(@UserIdInt int userId, ShortcutInfo shortcut) {
- if (shortcut.getBitmapPath() != null) {
- if (DEBUG) {
- Slog.d(TAG, "Removing " + shortcut.getBitmapPath());
- }
- new File(shortcut.getBitmapPath()).delete();
-
- shortcut.setBitmapPath(null);
- shortcut.setIconResourceId(0);
- shortcut.clearFlags(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES);
- }
+ // Do not remove the actual bitmap file yet, because if the device crashes before saving
+ // he XML we'd lose the icon. We just remove all dangling files after saving the XML.
+ shortcut.setIconResourceId(0);
+ shortcut.setIconResName(null);
+ shortcut.clearFlags(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES);
}
public void cleanupBitmapsForPackage(@UserIdInt int userId, String packageName) {
@@ -1013,6 +1157,58 @@
}
}
+ private void cleanupDanglingBitmapDirectoriesLocked(@UserIdInt int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "cleanupDanglingBitmaps: userId=" + userId);
+ }
+ final long start = injectElapsedRealtime();
+
+ final ShortcutUser user = getUserShortcutsLocked(userId);
+
+ final File bitmapDir = getUserBitmapFilePath(userId);
+ final File[] children = bitmapDir.listFiles();
+ if (children == null) {
+ return;
+ }
+ for (File child : children) {
+ if (!child.isDirectory()) {
+ continue;
+ }
+ final String packageName = child.getName();
+ if (DEBUG) {
+ Slog.d(TAG, "cleanupDanglingBitmaps: Found directory=" + packageName);
+ }
+ if (!user.hasPackage(packageName)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Removing dangling bitmap directory: " + packageName);
+ }
+ cleanupBitmapsForPackage(userId, packageName);
+ } else {
+ cleanupDanglingBitmapFilesLocked(userId, user, packageName, child);
+ }
+ }
+ logDurationStat(Stats.CLEANUP_DANGLING_BITMAPS, start);
+ }
+
+ private void cleanupDanglingBitmapFilesLocked(@UserIdInt int userId, @NonNull ShortcutUser user,
+ @NonNull String packageName, @NonNull File path) {
+ final ArraySet<String> usedFiles =
+ user.getPackageShortcuts(packageName).getUsedBitmapFiles();
+
+ for (File child : path.listFiles()) {
+ if (!child.isFile()) {
+ continue;
+ }
+ final String name = child.getName();
+ if (!usedFiles.contains(name)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Removing dangling bitmap file: " + child.getAbsolutePath());
+ }
+ child.delete();
+ }
+ }
+ }
+
@VisibleForTesting
static class FileOutputStreamWithPath extends FileOutputStream {
private final File mFile;
@@ -1036,7 +1232,7 @@
FileOutputStreamWithPath openIconFileForWrite(@UserIdInt int userId, ShortcutInfo shortcut)
throws IOException {
final File packagePath = new File(getUserBitmapFilePath(userId),
- shortcut.getPackageName());
+ shortcut.getPackage());
if (!packagePath.isDirectory()) {
packagePath.mkdirs();
if (!packagePath.isDirectory()) {
@@ -1046,7 +1242,7 @@
}
final String baseName = String.valueOf(injectCurrentTimeMillis());
- for (int suffix = 0;; suffix++) {
+ for (int suffix = 0; ; suffix++) {
final String filename = (suffix == 0 ? baseName : baseName + "_" + suffix) + ".png";
final File file = new File(packagePath, filename);
if (!file.exists()) {
@@ -1066,8 +1262,7 @@
final long token = injectClearCallingIdentity();
try {
// Clear icon info on the shortcut.
- shortcut.setIconResourceId(0);
- shortcut.setBitmapPath(null);
+ removeIcon(userId, shortcut);
final Icon icon = shortcut.getIcon();
if (icon == null) {
@@ -1075,7 +1270,6 @@
}
Bitmap bitmap;
- Bitmap bitmapToRecycle = null;
try {
switch (icon.getType()) {
case Icon.TYPE_RESOURCE: {
@@ -1119,7 +1313,7 @@
} finally {
IoUtils.closeQuietly(out);
}
- } catch (IOException|RuntimeException e) {
+ } catch (IOException | RuntimeException e) {
// STOPSHIP Change wtf to e
Slog.wtf(ShortcutService.TAG, "Unable to write bitmap to file", e);
if (path != null && path.exists()) {
@@ -1127,9 +1321,6 @@
}
}
} finally {
- if (bitmapToRecycle != null) {
- bitmapToRecycle.recycle();
- }
// Once saved, we won't use the original icon information, so null it out.
shortcut.clearIcon();
}
@@ -1142,7 +1333,7 @@
// so override in unit tests.
// TODO CTS this case.
void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) {
- if (!shortcut.getPackageName().equals(icon.getResPackage())) {
+ if (!shortcut.getPackage().equals(icon.getResPackage())) {
throw new IllegalArgumentException(
"Icon resource must reside in shortcut owner package");
}
@@ -1179,11 +1370,29 @@
return scaledBitmap;
}
+ /**
+ * For a shortcut, update all resource names from resource IDs, and also update all
+ * resource-based strings.
+ */
+ void fixUpShortcutResourceNamesAndValues(ShortcutInfo si) {
+ final Resources publisherRes = injectGetResourcesForApplicationAsUser(
+ si.getPackage(), si.getUserId());
+ if (publisherRes != null) {
+ final long start = injectElapsedRealtime();
+ try {
+ si.lookupAndFillInResourceNames(publisherRes);
+ } finally {
+ logDurationStat(Stats.RESOURCE_NAME_LOOKUP, start);
+ }
+ si.resolveResourceStrings(publisherRes);
+ }
+ }
+
// === Caller validation ===
private boolean isCallerSystem() {
final int callingUid = injectBinderCallingUid();
- return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID);
+ return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID);
}
private boolean isCallerShell() {
@@ -1192,26 +1401,39 @@
}
private void enforceSystemOrShell() {
- Preconditions.checkState(isCallerSystem() || isCallerShell(),
- "Caller must be system or shell");
+ if (!(isCallerSystem() || isCallerShell())) {
+ throw new SecurityException("Caller must be system or shell");
+ }
}
private void enforceShell() {
- Preconditions.checkState(isCallerShell(), "Caller must be shell");
+ if (!isCallerShell()) {
+ throw new SecurityException("Caller must be shell");
+ }
}
private void enforceSystem() {
- Preconditions.checkState(isCallerSystem(), "Caller must be system");
+ if (!isCallerSystem()) {
+ throw new SecurityException("Caller must be system");
+ }
}
private void enforceResetThrottlingPermission() {
if (isCallerSystem()) {
return;
}
- injectEnforceCallingPermission(
+ enforceCallingOrSelfPermission(
android.Manifest.permission.RESET_SHORTCUT_MANAGER_THROTTLING, null);
}
+ private void enforceCallingOrSelfPermission(
+ @NonNull String permission, @Nullable String message) {
+ if (isCallerSystem()) {
+ return;
+ }
+ injectEnforceCallingPermission(permission, message);
+ }
+
/**
* Somehow overriding ServiceContext.enforceCallingPermission() in the unit tests would confuse
* mockito. So instead we extracted it here and override it in the tests.
@@ -1241,20 +1463,29 @@
throw new SecurityException("Calling package name mismatch");
}
- void postToHandler(Runnable r) {
+ // Overridden in unit tests to execute r synchronously.
+ void injectPostToHandler(Runnable r) {
mHandler.post(r);
}
/**
- * Throw if {@code numShortcuts} is bigger than {@link #mMaxDynamicShortcuts}.
+ * @throws IllegalArgumentException if {@code numShortcuts} is bigger than
+ * {@link #getMaxActivityShortcuts()}.
*/
- void enforceMaxDynamicShortcuts(int numShortcuts) {
- if (numShortcuts > mMaxDynamicShortcuts) {
+ void enforceMaxActivityShortcuts(int numShortcuts) {
+ if (numShortcuts > mMaxShortcuts) {
throw new IllegalArgumentException("Max number of dynamic shortcuts exceeded");
}
}
/**
+ * Return the max number of dynamic + manifest shortcuts for each launcher icon.
+ */
+ int getMaxActivityShortcuts() {
+ return mMaxShortcuts;
+ }
+
+ /**
* - Sends a notification to LauncherApps
* - Write to file
*/
@@ -1268,17 +1499,21 @@
}
private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) {
- if (!mUserManager.isUserRunning(userId)) {
- return;
- }
- postToHandler(() -> {
- final ArrayList<ShortcutChangeListener> copy;
- synchronized (mLock) {
- copy = new ArrayList<>(mListeners);
- }
- // Note onShortcutChanged() needs to be called with the system service permissions.
- for (int i = copy.size() - 1; i >= 0; i--) {
- copy.get(i).onShortcutChanged(packageName, userId);
+ injectPostToHandler(() -> {
+ try {
+ final ArrayList<ShortcutChangeListener> copy;
+ synchronized (mLock) {
+ if (!isUserUnlockedL(userId)) {
+ return;
+ }
+
+ copy = new ArrayList<>(mListeners);
+ }
+ // Note onShortcutChanged() needs to be called with the system service permissions.
+ for (int i = copy.size() - 1; i >= 0; i--) {
+ copy.get(i).onShortcutChanged(packageName, userId);
+ }
+ } catch (Exception ignore) {
}
});
}
@@ -1287,72 +1522,61 @@
* Clean up / validate an incoming shortcut.
* - Make sure all mandatory fields are set.
* - Make sure the intent's extras are persistable, and them to set
- * {@link ShortcutInfo#mIntentPersistableExtras}. Also clear its extras.
+ * {@link ShortcutInfo#mIntentPersistableExtrases}. Also clear its extras.
* - Clear flags.
*
* TODO Detailed unit tests
*/
private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) {
Preconditions.checkNotNull(shortcut, "Null shortcut detected");
- if (shortcut.getActivityComponent() != null) {
+ if (shortcut.getActivity() != null) {
Preconditions.checkState(
- shortcut.getPackageName().equals(
- shortcut.getActivityComponent().getPackageName()),
- "Activity package name mismatch");
+ shortcut.getPackage().equals(shortcut.getActivity().getPackageName()),
+ "Cannot publish shortcut: activity " + shortcut.getActivity() + " does not"
+ + " belong to package " + shortcut.getPackage());
+ Preconditions.checkState(
+ injectIsMainActivity(shortcut.getActivity(), shortcut.getUserId()),
+ "Cannot publish shortcut: activity " + shortcut.getActivity() + " is not"
+ + " main activity");
}
if (!forUpdate) {
shortcut.enforceMandatoryFields();
+ Preconditions.checkArgument(
+ injectIsMainActivity(shortcut.getActivity(), shortcut.getUserId()),
+ "Cannot publish shortcut: " + shortcut.getActivity() + " is not main activity");
}
if (shortcut.getIcon() != null) {
ShortcutInfo.validateIcon(shortcut.getIcon());
}
- validateForXml(shortcut.getId());
- validateForXml(shortcut.getTitle());
- validatePersistableBundleForXml(shortcut.getIntentPersistableExtras());
- validatePersistableBundleForXml(shortcut.getExtras());
-
shortcut.replaceFlags(0);
}
- // KXmlSerializer is strict and doesn't allow certain characters, so we disallow those
- // characters.
+ /**
+ * When a shortcut has no target activity, set the default one from the package.
+ */
+ private void fillInDefaultActivity(List<ShortcutInfo> shortcuts) {
- private static void validatePersistableBundleForXml(PersistableBundle b) {
- if (b == null || b.size() == 0) {
- return;
- }
- for (String key : b.keySet()) {
- validateForXml(key);
- final Object value = b.get(key);
- if (value == null) {
- continue;
- } else if (value instanceof String) {
- validateForXml((String) value);
- } else if (value instanceof String[]) {
- for (String v : (String[]) value) {
- validateForXml(v);
+ ComponentName defaultActivity = null;
+ for (int i = shortcuts.size() - 1; i >= 0; i--) {
+ final ShortcutInfo si = shortcuts.get(i);
+ if (si.getActivity() == null) {
+ if (defaultActivity == null) {
+ defaultActivity = injectGetDefaultMainActivity(
+ si.getPackage(), si.getUserId());
+ Preconditions.checkState(defaultActivity != null,
+ "Launcher activity not found for package " + si.getPackage());
}
- } else if (value instanceof PersistableBundle) {
- validatePersistableBundleForXml((PersistableBundle) value);
+ si.setActivity(defaultActivity);
}
}
}
- private static void validateForXml(String s) {
- if (TextUtils.isEmpty(s)) {
- return;
+ private void assignImplicitRanks(List<ShortcutInfo> shortcuts) {
+ for (int i = shortcuts.size() - 1; i >= 0; i--) {
+ shortcuts.get(i).setImplicitRank(i);
}
- for (int i = s.length() - 1; i >= 0; i--) {
- if (!isAllowedInXml(s.charAt(i))) {
- throw new IllegalArgumentException("Unsupported character detected in: " + s);
- }
- }
- }
-
- private static boolean isAllowedInXml(char c) {
- return (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd);
}
// === APIs ===
@@ -1366,29 +1590,46 @@
final int size = newShortcuts.size();
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
+ ps.getUser().onCalledByPublisher(packageName);
+
+ ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
+
+ fillInDefaultActivity(newShortcuts);
+
+ ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_SET);
// Throttling.
- if (!ps.tryApiCall(this)) {
+ if (!ps.tryApiCall()) {
return false;
}
- enforceMaxDynamicShortcuts(size);
- // Validate the shortcuts.
+ // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
+ ps.clearAllImplicitRanks();
+ assignImplicitRanks(newShortcuts);
+
for (int i = 0; i < size; i++) {
fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false);
}
// First, remove all un-pinned; dynamic shortcuts
- ps.deleteAllDynamicShortcuts(this);
+ ps.deleteAllDynamicShortcuts();
// Then, add/update all. We need to make sure to take over "pinned" flag.
for (int i = 0; i < size; i++) {
final ShortcutInfo newShortcut = newShortcuts.get(i);
- ps.addDynamicShortcut(this, newShortcut);
+ ps.addOrUpdateDynamicShortcut(newShortcut);
}
+
+ // Lastly, adjust the ranks.
+ ps.adjustRanks();
}
packageShortcutsChanged(packageName, userId);
+
+ verifyStates();
+
return true;
}
@@ -1401,34 +1642,75 @@
final int size = newShortcuts.size();
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
+ ps.getUser().onCalledByPublisher(packageName);
+
+ ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
+
+ // For update, don't fill in the default activity. Having null activity means
+ // "don't update the activity" here.
+
+ ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_UPDATE);
// Throttling.
- if (!ps.tryApiCall(this)) {
+ if (!ps.tryApiCall()) {
return false;
}
+ // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
+ ps.clearAllImplicitRanks();
+ assignImplicitRanks(newShortcuts);
+
for (int i = 0; i < size; i++) {
final ShortcutInfo source = newShortcuts.get(i);
fixUpIncomingShortcutInfo(source, /* forUpdate= */ true);
final ShortcutInfo target = ps.findShortcutById(source.getId());
- if (target != null) {
- final boolean replacingIcon = (source.getIcon() != null);
- if (replacingIcon) {
- removeIcon(userId, target);
- }
+ if (target == null) {
+ continue;
+ }
- target.copyNonNullFieldsFrom(source);
+ if (target.isEnabled() != source.isEnabled()) {
+ Slog.w(TAG,
+ "ShortcutInfo.enabled cannot be changed with updateShortcuts()");
+ }
- if (replacingIcon) {
- saveIconAndFixUpShortcut(userId, target);
- }
+ // When updating the rank, we need to insert between existing ranks, so set
+ // this setRankChanged, and also copy the implicit rank fo adjustRanks().
+ if (source.hasRank()) {
+ target.setRankChanged();
+ target.setImplicitRank(source.getImplicitRank());
+ }
+
+ final boolean replacingIcon = (source.getIcon() != null);
+ if (replacingIcon) {
+ removeIcon(userId, target);
+ }
+
+ // Note copyNonNullFieldsFrom() does the "updatable with?" check too.
+ target.copyNonNullFieldsFrom(source);
+ target.setTimestamp(injectCurrentTimeMillis());
+
+ if (replacingIcon) {
+ saveIconAndFixUpShortcut(userId, target);
+ }
+
+ // When we're updating any resource related fields, re-extract the res names and
+ // the values.
+ if (replacingIcon || source.hasStringResources()) {
+ fixUpShortcutResourceNamesAndValues(target);
}
}
+
+ // Lastly, adjust the ranks.
+ ps.adjustRanks();
}
packageShortcutsChanged(packageName, userId);
+ verifyStates();
+
return true;
}
@@ -1441,10 +1723,23 @@
final int size = newShortcuts.size();
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
+ ps.getUser().onCalledByPublisher(packageName);
+
+ ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
+
+ fillInDefaultActivity(newShortcuts);
+
+ ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_ADD);
+
+ // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
+ ps.clearAllImplicitRanks();
+ assignImplicitRanks(newShortcuts);
// Throttling.
- if (!ps.tryApiCall(this)) {
+ if (!ps.tryApiCall()) {
return false;
}
for (int i = 0; i < size; i++) {
@@ -1453,13 +1748,75 @@
// Validate the shortcut.
fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false);
+ // When ranks are changing, we need to insert between ranks, so set the
+ // "rank changed" flag.
+ newShortcut.setRankChanged();
+
// Add it.
- ps.addDynamicShortcut(this, newShortcut);
+ ps.addOrUpdateDynamicShortcut(newShortcut);
+ }
+
+ // Lastly, adjust the ranks.
+ ps.adjustRanks();
+ }
+ packageShortcutsChanged(packageName, userId);
+
+ verifyStates();
+
+ return true;
+ }
+
+ @Override
+ public void disableShortcuts(String packageName, List shortcutIds,
+ CharSequence disabledMessage, int disabledMessageResId, @UserIdInt int userId) {
+ verifyCaller(packageName, userId);
+ Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
+
+ synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
+ final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
+ ps.getUser().onCalledByPublisher(packageName);
+
+ ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
+
+ final String disabledMessageString =
+ (disabledMessage == null) ? null : disabledMessage.toString();
+
+ for (int i = shortcutIds.size() - 1; i >= 0; i--) {
+ ps.disableWithId(Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)),
+ disabledMessageString, disabledMessageResId,
+ /* overrideImmutable=*/ false);
+ }
+
+ // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks.
+ ps.adjustRanks();
+ }
+ packageShortcutsChanged(packageName, userId);
+
+ verifyStates();
+ }
+
+ @Override
+ public void enableShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) {
+ verifyCaller(packageName, userId);
+ Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
+
+ synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
+ final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
+ ps.getUser().onCalledByPublisher(packageName);
+
+ ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
+
+ for (int i = shortcutIds.size() - 1; i >= 0; i--) {
+ ps.enableWithId((String) shortcutIds.get(i));
}
}
packageShortcutsChanged(packageName, userId);
- return true;
+ verifyStates();
}
@Override
@@ -1469,12 +1826,24 @@
Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
+ final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
+ ps.getUser().onCalledByPublisher(packageName);
+
+ ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
+
for (int i = shortcutIds.size() - 1; i >= 0; i--) {
- getPackageShortcutsLocked(packageName, userId).deleteDynamicWithId(this,
+ ps.deleteDynamicWithId(
Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)));
}
+
+ // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks.
+ ps.adjustRanks();
}
packageShortcutsChanged(packageName, userId);
+
+ verifyStates();
}
@Override
@@ -1482,16 +1851,25 @@
verifyCaller(packageName, userId);
synchronized (mLock) {
- getPackageShortcutsLocked(packageName, userId).deleteAllDynamicShortcuts(this);
+ throwIfUserLockedL(userId);
+
+ final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
+ ps.getUser().onCalledByPublisher(packageName);
+ ps.deleteAllDynamicShortcuts();
}
packageShortcutsChanged(packageName, userId);
+
+ verifyStates();
}
@Override
public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
+
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
return getShortcutsWithQueryLocked(
packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
ShortcutInfo::isDynamic);
@@ -1499,10 +1877,27 @@
}
@Override
+ public ParceledListSlice<ShortcutInfo> getManifestShortcuts(String packageName,
+ @UserIdInt int userId) {
+ verifyCaller(packageName, userId);
+
+ synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
+ return getShortcutsWithQueryLocked(
+ packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
+ ShortcutInfo::isManifestShortcut);
+ }
+ }
+
+ @Override
public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
+
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
return getShortcutsWithQueryLocked(
packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
ShortcutInfo::isPinned);
@@ -1514,17 +1909,19 @@
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
- getPackageShortcutsLocked(packageName, userId).findAll(this, ret, query, cloneFlags);
+ final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
+ ps.getUser().onCalledByPublisher(packageName);
+ ps.findAll(ret, query, cloneFlags);
return new ParceledListSlice<>(ret);
}
@Override
- public int getMaxDynamicShortcutCount(String packageName, @UserIdInt int userId)
+ public int getMaxShortcutCountPerActivity(String packageName, @UserIdInt int userId)
throws RemoteException {
verifyCaller(packageName, userId);
- return mMaxDynamicShortcuts;
+ return mMaxShortcuts;
}
@Override
@@ -1532,8 +1929,11 @@
verifyCaller(packageName, userId);
synchronized (mLock) {
- return mMaxUpdatesPerInterval
- - getPackageShortcutsLocked(packageName, userId).getApiCallCount(this);
+ throwIfUserLockedL(userId);
+
+ final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
+ ps.getUser().onCalledByPublisher(packageName);
+ return mMaxUpdatesPerInterval - ps.getApiCallCount();
}
}
@@ -1542,12 +1942,14 @@
verifyCaller(packageName, userId);
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
return getNextResetTimeLocked();
}
}
@Override
- public int getIconMaxDimensions(String packageName, int userId) throws RemoteException {
+ public int getIconMaxDimensions(String packageName, int userId) {
verifyCaller(packageName, userId);
synchronized (mLock) {
@@ -1555,8 +1957,41 @@
}
}
+ @Override
+ public void reportShortcutUsed(String packageName, String shortcutId, int userId) {
+ verifyCaller(packageName, userId);
+
+ Preconditions.checkNotNull(shortcutId);
+
+ if (DEBUG) {
+ Slog.d(TAG, String.format("reportShortcutUsed: Shortcut %s package %s used on user %d",
+ shortcutId, packageName, userId));
+ }
+
+ synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
+ final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
+ ps.getUser().onCalledByPublisher(packageName);
+
+ if (ps.findShortcutById(shortcutId) == null) {
+ Log.w(TAG, String.format("reportShortcutUsed: package %s doesn't have shortcut %s",
+ packageName, shortcutId));
+ return;
+ }
+ }
+
+ final long token = injectClearCallingIdentity();
+ try {
+ mUsageStatsManagerInternal.reportShortcutUsage(packageName, shortcutId, userId);
+ } finally {
+ injectRestoreCallingIdentity(token);
+ }
+ }
+
/**
- * Reset all throttling, for developer options and command line. Only system/shell can call it.
+ * Reset all throttling, for developer options and command line. Only system/shell can call
+ * it.
*/
@Override
public void resetThrottling() {
@@ -1567,6 +2002,11 @@
void resetThrottlingInner(@UserIdInt int userId) {
synchronized (mLock) {
+ if (!isUserUnlockedL(userId)) {
+ Log.w(TAG, "User " + userId + " is locked or not running");
+ return;
+ }
+
getUserShortcutsLocked(userId).resetThrottling();
}
scheduleSaveUser(userId);
@@ -1581,26 +2021,33 @@
Slog.i(TAG, "ShortcutManager: throttling counter reset for all users");
}
- void resetPackageThrottling(String packageName, int userId) {
- synchronized (mLock) {
- getPackageShortcutsLocked(packageName, userId)
- .resetRateLimitingForCommandLineNoSaving();
- saveUserLocked(userId);
- }
- }
-
@Override
public void onApplicationActive(String packageName, int userId) {
if (DEBUG) {
Slog.d(TAG, "onApplicationActive: package=" + packageName + " userid=" + userId);
}
enforceResetThrottlingPermission();
- resetPackageThrottling(packageName, userId);
+
+ synchronized (mLock) {
+ if (!isUserUnlockedL(userId)) {
+ // This is called by system UI, so no need to throw. Just ignore.
+ return;
+ }
+
+ getPackageShortcutsLocked(packageName, userId)
+ .resetRateLimitingForCommandLineNoSaving();
+ saveUserLocked(userId);
+ }
}
// We override this method in unit tests to do a simpler check.
boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
- return hasShortcutHostPermissionInner(callingPackage, userId);
+ final long start = injectElapsedRealtime();
+ try {
+ return hasShortcutHostPermissionInner(callingPackage, userId);
+ } finally {
+ logDurationStat(Stats.LAUNCHER_PERMISSION_CHECK, start);
+ }
}
// This method is extracted so we can directly call this method from unit tests,
@@ -1608,15 +2055,24 @@
@VisibleForTesting
boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) {
synchronized (mLock) {
- final long start = System.currentTimeMillis();
+ throwIfUserLockedL(userId);
final ShortcutUser user = getUserShortcutsLocked(userId);
+ // Always trust the in-memory cache.
+ final ComponentName cached = user.getCachedLauncher();
+ if (cached != null) {
+ if (cached.getPackageName().equals(callingPackage)) {
+ return true;
+ }
+ }
+ // If the cached one doesn't match, then go ahead
+
final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
// Default launcher from package manager.
- final long startGetHomeActivitiesAsUser = System.currentTimeMillis();
- final ComponentName defaultLauncher = injectPackageManagerInternal()
+ final long startGetHomeActivitiesAsUser = injectElapsedRealtime();
+ final ComponentName defaultLauncher = mPackageManagerInternal
.getHomeActivitiesAsUser(allHomeCandidates, userId);
logDurationStat(Stats.GET_DEFAULT_HOME, startGetHomeActivitiesAsUser);
@@ -1627,11 +2083,18 @@
Slog.v(TAG, "Default launcher from PM: " + detected);
}
} else {
- detected = user.getLauncherComponent();
+ detected = user.getLastKnownLauncher();
- // TODO: Make sure it's still enabled.
- if (DEBUG) {
- Slog.v(TAG, "Cached launcher: " + detected);
+ if (detected != null) {
+ if (injectIsActivityEnabledAndExported(detected, userId)) {
+ if (DEBUG) {
+ Slog.v(TAG, "Cached launcher: " + detected);
+ }
+ } else {
+ Slog.w(TAG, "Cached launcher " + detected + " no longer exists");
+ detected = null;
+ user.clearLauncher();
+ }
}
}
@@ -1661,13 +2124,13 @@
lastPriority = ri.priority;
}
}
- logDurationStat(Stats.LAUNCHER_PERMISSION_CHECK, start);
+ // Update the cache.
+ user.setLauncher(detected);
if (detected != null) {
if (DEBUG) {
Slog.v(TAG, "Detected launcher: " + detected);
}
- user.setLauncherComponent(this, detected);
return detected.getPackageName().equals(callingPackage);
} else {
// Default launcher not found.
@@ -1678,10 +2141,12 @@
// === House keeping ===
- private void cleanUpPackageForAllLoadedUsers(String packageName, @UserIdInt int packageUserId) {
+ private void cleanUpPackageForAllLoadedUsers(String packageName, @UserIdInt int packageUserId,
+ boolean appStillExists) {
synchronized (mLock) {
forEachLoadedUserLocked(user ->
- cleanUpPackageLocked(packageName, user.getUserId(), packageUserId));
+ cleanUpPackageLocked(packageName, user.getUserId(), packageUserId,
+ appStillExists));
}
}
@@ -1693,7 +2158,8 @@
* This is called when an app is uninstalled, or an app gets "clear data"ed.
*/
@VisibleForTesting
- void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId) {
+ void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId,
+ boolean appStillExists) {
final boolean wasUserLoaded = isUserLoadedLocked(owningUserId);
final ShortcutUser user = getUserShortcutsLocked(owningUserId);
@@ -1701,7 +2167,7 @@
// First, remove the package from the package list (if the package is a publisher).
if (packageUserId == owningUserId) {
- if (user.removePackage(this, packageName) != null) {
+ if (user.removePackage(packageName) != null) {
doNotify = true;
}
}
@@ -1714,7 +2180,7 @@
// Now there may be orphan shortcuts because we removed pinned shortcuts at the previous
// step. Remove them too.
- user.forAllPackages(p -> p.refreshPinnedFlags(this));
+ user.forAllPackages(p -> p.refreshPinnedFlags());
scheduleSaveUser(owningUserId);
@@ -1722,6 +2188,13 @@
notifyListeners(packageName, owningUserId);
}
+ // If the app still exists (i.e. data cleared), we need to re-publish manifest shortcuts.
+ if (appStillExists && (packageUserId == owningUserId)) {
+ // This will do the notification and save when needed, so do it after the above
+ // notifyListeners.
+ user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true);
+ }
+
if (!wasUserLoaded) {
// Note this will execute the scheduled save.
unloadUserLocked(owningUserId);
@@ -1740,17 +2213,21 @@
@Nullable ComponentName componentName,
int queryFlags, int userId) {
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
- final int cloneFlag =
- ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) == 0)
- ? ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER
- : ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO;
+
+ final boolean cloneKeyFieldOnly =
+ ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0);
+ final int cloneFlag = cloneKeyFieldOnly ? ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO
+ : ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER;
if (packageName == null) {
shortcutIds = null; // LauncherAppsService already threw for it though.
}
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
+
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
- .attemptToRestoreIfNeededAndSave(ShortcutService.this);
+ .attemptToRestoreIfNeededAndSave();
if (packageName != null) {
getShortcutsInnerLocked(launcherUserId,
@@ -1775,7 +2252,13 @@
final ArraySet<String> ids = shortcutIds == null ? null
: new ArraySet<>(shortcutIds);
- getPackageShortcutsLocked(packageName, userId).findAll(ShortcutService.this, ret,
+ final ShortcutPackage p = getUserShortcutsLocked(userId)
+ .getPackageShortcutsIfExists(packageName);
+ if (p == null) {
+ return; // No need to instantiate ShortcutPackage.
+ }
+
+ p.findAll(ret,
(ShortcutInfo si) -> {
if (si.getLastChangedTimestamp() < changedSince) {
return false;
@@ -1783,17 +2266,25 @@
if (ids != null && !ids.contains(si.getId())) {
return false;
}
- if (componentName != null
- && !componentName.equals(si.getActivityComponent())) {
- return false;
+ if (componentName != null) {
+ if (si.getActivity() != null
+ && !si.getActivity().equals(componentName)) {
+ return false;
+ }
}
- final boolean matchDynamic =
- ((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0)
- && si.isDynamic();
- final boolean matchPinned =
- ((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0)
- && si.isPinned();
- return matchDynamic || matchPinned;
+ if (((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0)
+ && si.isDynamic()) {
+ return true;
+ }
+ if (((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0)
+ && si.isPinned()) {
+ return true;
+ }
+ if (((queryFlags & ShortcutQuery.FLAG_GET_MANIFEST) != 0)
+ && si.isManifestShortcut()) {
+ return true;
+ }
+ return false;
}, cloneFlag, callingPackage, launcherUserId);
}
@@ -1804,8 +2295,11 @@
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
+
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
- .attemptToRestoreIfNeededAndSave(ShortcutService.this);
+ .attemptToRestoreIfNeededAndSave();
final ShortcutInfo si = getShortcutInfoLocked(
launcherUserId, callingPackage, packageName, shortcutId, userId);
@@ -1819,9 +2313,17 @@
Preconditions.checkStringNotEmpty(packageName, "packageName");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
+
+ final ShortcutPackage p = getUserShortcutsLocked(userId)
+ .getPackageShortcutsIfExists(packageName);
+ if (p == null) {
+ return null;
+ }
+
final ArrayList<ShortcutInfo> list = new ArrayList<>(1);
- getPackageShortcutsLocked(packageName, userId).findAll(
- ShortcutService.this, list,
+ p.findAll(list,
(ShortcutInfo si) -> shortcutId.equals(si.getId()),
/* clone flags=*/ 0, callingPackage, launcherUserId);
return list.size() == 0 ? null : list.get(0);
@@ -1836,18 +2338,22 @@
Preconditions.checkNotNull(shortcutIds, "shortcutIds");
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
+
final ShortcutLauncher launcher =
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId);
- launcher.attemptToRestoreIfNeededAndSave(ShortcutService.this);
+ launcher.attemptToRestoreIfNeededAndSave();
- launcher.pinShortcuts(
- ShortcutService.this, userId, packageName, shortcutIds);
+ launcher.pinShortcuts(userId, packageName, shortcutIds);
}
packageShortcutsChanged(packageName, userId);
+
+ verifyStates();
}
@Override
- public Intent createShortcutIntent(int launcherUserId,
+ public Intent[] createShortcutIntents(int launcherUserId,
@NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId) {
// Calling permission must be checked by LauncherAppsImpl.
@@ -1855,17 +2361,21 @@
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
+
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
- .attemptToRestoreIfNeededAndSave(ShortcutService.this);
+ .attemptToRestoreIfNeededAndSave();
// Make sure the shortcut is actually visible to the launcher.
final ShortcutInfo si = getShortcutInfoLocked(
launcherUserId, callingPackage, packageName, shortcutId, userId);
// "si == null" should suffice here, but check the flags too just to make sure.
- if (si == null || !(si.isDynamic() || si.isPinned())) {
+ if (si == null || !si.isEnabled() || !si.isAlive()) {
+ Log.e(TAG, "Shortcut " + shortcutId + " does not exist or disabled");
return null;
}
- return si.getIntent();
+ return si.getIntents();
}
}
@@ -1884,11 +2394,19 @@
Preconditions.checkNotNull(shortcutId, "shortcutId");
synchronized (mLock) {
- getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
- .attemptToRestoreIfNeededAndSave(ShortcutService.this);
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
- final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
- packageName, userId).findShortcutById(shortcutId);
+ getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
+ .attemptToRestoreIfNeededAndSave();
+
+ final ShortcutPackage p = getUserShortcutsLocked(userId)
+ .getPackageShortcutsIfExists(packageName);
+ if (p == null) {
+ return 0;
+ }
+
+ final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
return (shortcutInfo != null && shortcutInfo.hasIconResource())
? shortcutInfo.getIconResourceId() : 0;
}
@@ -1903,11 +2421,19 @@
Preconditions.checkNotNull(shortcutId, "shortcutId");
synchronized (mLock) {
- getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
- .attemptToRestoreIfNeededAndSave(ShortcutService.this);
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
- final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
- packageName, userId).findShortcutById(shortcutId);
+ getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
+ .attemptToRestoreIfNeededAndSave();
+
+ final ShortcutPackage p = getUserShortcutsLocked(userId)
+ .getPackageShortcutsIfExists(packageName);
+ if (p == null) {
+ return null;
+ }
+
+ final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
if (shortcutInfo == null || !shortcutInfo.hasIconFile()) {
return null;
}
@@ -1931,30 +2457,37 @@
@NonNull String callingPackage) {
return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId);
}
+ }
- /**
- * Called by AM when the system locale changes *within the AM lock. ABSOLUTELY do not take
- * any locks in this method.
- */
+ final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
- public void onSystemLocaleChangedNoLock() {
- if (!FEATURE_ENABLED) {
- return;
+ public void onReceive(Context context, Intent intent) {
+ if (!mBootCompleted.get()) {
+ return; // Boot not completed, ignore the broadcast.
}
- // DO NOT HOLD ANY LOCKS HERE.
+ try {
+ if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
+ handleLocaleChanged();
+ }
+ } catch (Exception e) {
+ wtf("Exception in mReceiver.onReceive", e);
+ }
+ }
+ };
- // We want to reset throttling for all packages for all users. But we can't just do so
- // here because:
- // - We can't load/save users that are locked.
- // - Even for loaded users, resetting the counters would require us to hold mLock.
- //
- // So we use a "pull" model instead. In here, we just increment the "locale change
- // sequence number". Each ShortcutUser has the "last known locale change sequence".
- //
- // This allows ShortcutUser's to detect the system locale change, so they can reset
- // counters.
- mLocaleChangeSequenceNumber.incrementAndGet();
- postToHandler(() -> scheduleSaveBaseState());
+ void handleLocaleChanged() {
+ if (DEBUG) {
+ Slog.d(TAG, "handleLocaleChanged");
+ }
+ scheduleSaveBaseState();
+
+ synchronized (mLock) {
+ final long token = injectClearCallingIdentity();
+ try {
+ forEachLoadedUserLocked(user -> user.detectLocaleChange());
+ } finally {
+ injectRestoreCallingIdentity(token);
+ }
}
}
@@ -1962,25 +2495,76 @@
* Package event callbacks.
*/
@VisibleForTesting
- final PackageMonitor mPackageMonitor = new PackageMonitor() {
+ final BroadcastReceiver mPackageMonitor = new BroadcastReceiver() {
@Override
- public void onPackageAdded(String packageName, int uid) {
- handlePackageAdded(packageName, getChangingUserId());
- }
+ public void onReceive(Context context, Intent intent) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId == UserHandle.USER_NULL) {
+ Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
+ return;
+ }
- @Override
- public void onPackageUpdateFinished(String packageName, int uid) {
- handlePackageUpdateFinished(packageName, getChangingUserId());
- }
+ final String action = intent.getAction();
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- handlePackageRemoved(packageName, getChangingUserId());
- }
+ // This is normally called on Handler, so clearCallingIdentity() isn't needed,
+ // but we still check it in unit tests.
+ final long token = injectClearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ if (!isUserUnlockedL(userId)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring package broadcast " + action
+ + " for locked/stopped user " + userId);
+ }
+ return;
+ }
- @Override
- public void onPackageDataCleared(String packageName, int uid) {
- handlePackageDataCleared(packageName, getChangingUserId());
+ // Whenever we get one of those package broadcasts, or get
+ // ACTION_PREFERRED_ACTIVITY_CHANGED, we purge the default launcher cache.
+ final ShortcutUser user = getUserShortcutsLocked(userId);
+ user.clearLauncher();
+ }
+ if (Intent.ACTION_PREFERRED_ACTIVITY_CHANGED.equals(action)) {
+ // Nothing farther to do.
+ return;
+ }
+
+ final Uri intentUri = intent.getData();
+ final String packageName = (intentUri != null) ? intentUri.getSchemeSpecificPart()
+ : null;
+ if (packageName == null) {
+ Slog.w(TAG, "Intent broadcast does not contain package name: " + intent);
+ return;
+ }
+
+ final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+
+ switch (action) {
+ case Intent.ACTION_PACKAGE_ADDED:
+ if (replacing) {
+ handlePackageUpdateFinished(packageName, userId);
+ } else {
+ handlePackageAdded(packageName, userId);
+ }
+ break;
+ case Intent.ACTION_PACKAGE_REMOVED:
+ if (!replacing) {
+ handlePackageRemoved(packageName, userId);
+ }
+ break;
+ case Intent.ACTION_PACKAGE_CHANGED:
+ handlePackageChanged(packageName, userId);
+
+ break;
+ case Intent.ACTION_PACKAGE_DATA_CLEARED:
+ handlePackageDataCleared(packageName, userId);
+ break;
+ }
+ } catch (Exception e) {
+ wtf("Exception in mPackageMonitor.onReceive", e);
+ } finally {
+ injectRestoreCallingIdentity(token);
+ }
}
};
@@ -1988,39 +2572,71 @@
* Called when a user is unlocked.
* - Check all known packages still exist, and otherwise perform cleanup.
* - If a package still exists, check the version code. If it's been updated, may need to
- * update timestamps of its shortcuts.
+ * update timestamps of its shortcuts.
*/
@VisibleForTesting
void checkPackageChanges(@UserIdInt int ownerUserId) {
if (DEBUG) {
Slog.d(TAG, "checkPackageChanges() ownerUserId=" + ownerUserId);
}
- final ArrayList<PackageWithUser> gonePackages = new ArrayList<>();
-
- synchronized (mLock) {
- final ShortcutUser user = getUserShortcutsLocked(ownerUserId);
-
- user.forAllPackageItems(spi -> {
- if (spi.getPackageInfo().isShadow()) {
- return; // Don't delete shadow information.
- }
- final int versionCode = getApplicationVersionCode(
- spi.getPackageName(), spi.getPackageUserId());
- if (versionCode >= 0) {
- // Package still installed, see if it's updated.
- getUserShortcutsLocked(ownerUserId).handlePackageUpdated(
- this, spi.getPackageName(), versionCode);
- } else {
- gonePackages.add(PackageWithUser.of(spi));
- }
- });
- if (gonePackages.size() > 0) {
- for (int i = gonePackages.size() - 1; i >= 0; i--) {
- final PackageWithUser pu = gonePackages.get(i);
- cleanUpPackageLocked(pu.packageName, ownerUserId, pu.userId);
- }
- }
+ if (injectIsSafeModeEnabled()) {
+ Slog.i(TAG, "Safe mode, skipping checkPackageChanges()");
+ return;
}
+
+ final long start = injectElapsedRealtime();
+ try {
+ final ArrayList<PackageWithUser> gonePackages = new ArrayList<>();
+
+ synchronized (mLock) {
+ final ShortcutUser user = getUserShortcutsLocked(ownerUserId);
+
+ // Find packages that have been uninstalled.
+ user.forAllPackageItems(spi -> {
+ if (spi.getPackageInfo().isShadow()) {
+ return; // Don't delete shadow information.
+ }
+ if (!isPackageInstalled(spi.getPackageName(), spi.getPackageUserId())) {
+ if (DEBUG) {
+ Slog.d(TAG, "Uninstalled: " + spi.getPackageName()
+ + " user " + spi.getPackageUserId());
+ }
+ gonePackages.add(PackageWithUser.of(spi));
+ }
+ });
+ if (gonePackages.size() > 0) {
+ for (int i = gonePackages.size() - 1; i >= 0; i--) {
+ final PackageWithUser pu = gonePackages.get(i);
+ cleanUpPackageLocked(pu.packageName, ownerUserId, pu.userId,
+ /* appStillExists = */ false);
+ }
+ }
+
+ rescanUpdatedPackagesLocked(ownerUserId, user.getLastAppScanTime(),
+ /* forceRescan=*/ false);
+ }
+ } finally {
+ logDurationStat(Stats.CHECK_PACKAGE_CHANGES, start);
+ }
+ verifyStates();
+ }
+
+ private void rescanUpdatedPackagesLocked(@UserIdInt int userId, long lastScanTime,
+ boolean forceRescan) {
+ final ShortcutUser user = getUserShortcutsLocked(userId);
+
+ final long now = injectCurrentTimeMillis();
+
+ // Then for each installed app, publish manifest shortcuts when needed.
+ forUpdatedPackages(userId, lastScanTime, ai -> {
+ user.attemptToRestoreIfNeededAndSave(this, ai.packageName, userId);
+ user.rescanPackageIfNeeded(ai.packageName, forceRescan);
+ });
+
+ // Write the time just before the scan, because there may be apps that have just
+ // been updated, and we want to catch them in the next time.
+ user.setLastAppScanTime(now);
+ scheduleSaveUser(userId);
}
private void handlePackageAdded(String packageName, @UserIdInt int userId) {
@@ -2028,9 +2644,11 @@
Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId));
}
synchronized (mLock) {
- forEachLoadedUserLocked(user ->
- user.attemptToRestoreIfNeededAndSave(this, packageName, userId));
+ final ShortcutUser user = getUserShortcutsLocked(userId);
+ user.attemptToRestoreIfNeededAndSave(this, packageName, userId);
+ user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true);
}
+ verifyStates();
}
private void handlePackageUpdateFinished(String packageName, @UserIdInt int userId) {
@@ -2039,15 +2657,14 @@
packageName, userId));
}
synchronized (mLock) {
- forEachLoadedUserLocked(user ->
- user.attemptToRestoreIfNeededAndSave(this, packageName, userId));
+ final ShortcutUser user = getUserShortcutsLocked(userId);
+ user.attemptToRestoreIfNeededAndSave(this, packageName, userId);
- final int versionCode = getApplicationVersionCode(packageName, userId);
- if (versionCode < 0) {
- return; // shouldn't happen
+ if (isPackageInstalled(packageName, userId)) {
+ user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true);
}
- getUserShortcutsLocked(userId).handlePackageUpdated(this, packageName, versionCode);
}
+ verifyStates();
}
private void handlePackageRemoved(String packageName, @UserIdInt int packageUserId) {
@@ -2055,7 +2672,9 @@
Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName,
packageUserId));
}
- cleanUpPackageForAllLoadedUsers(packageName, packageUserId);
+ cleanUpPackageForAllLoadedUsers(packageName, packageUserId, /* appStillExists = */ false);
+
+ verifyStates();
}
private void handlePackageDataCleared(String packageName, int packageUserId) {
@@ -2063,20 +2682,49 @@
Slog.d(TAG, String.format("handlePackageDataCleared: %s user=%d", packageName,
packageUserId));
}
- cleanUpPackageForAllLoadedUsers(packageName, packageUserId);
+ cleanUpPackageForAllLoadedUsers(packageName, packageUserId, /* appStillExists = */ true);
+
+ verifyStates();
+ }
+
+ private void handlePackageChanged(String packageName, int packageUserId) {
+ if (DEBUG) {
+ Slog.d(TAG, String.format("handlePackageChanged: %s user=%d", packageName,
+ packageUserId));
+ }
+
+ // Activities may be disabled or enabled. Just rescan the package.
+ synchronized (mLock) {
+ final ShortcutUser user = getUserShortcutsLocked(packageUserId);
+
+ user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true);
+ }
+
+ verifyStates();
}
// === PackageManager interaction ===
- PackageInfo getPackageInfoWithSignatures(String packageName, @UserIdInt int userId) {
- return injectPackageInfo(packageName, userId, true);
+ /**
+ * Returns {@link PackageInfo} unless it's uninstalled or disabled.
+ */
+ @Nullable
+ final PackageInfo getPackageInfoWithSignatures(String packageName, @UserIdInt int userId) {
+ return getPackageInfo(packageName, userId, true);
+ }
+
+ /**
+ * Returns {@link PackageInfo} unless it's uninstalled or disabled.
+ */
+ @Nullable
+ final PackageInfo getPackageInfo(String packageName, @UserIdInt int userId) {
+ return getPackageInfo(packageName, userId, false);
}
int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) {
final long token = injectClearCallingIdentity();
try {
- return mIPackageManager.getPackageUid(packageName, PACKAGE_MATCH_FLAGS
- , userId);
+ return mIPackageManager.getPackageUid(packageName, PACKAGE_MATCH_FLAGS, userId);
} catch (RemoteException e) {
// Shouldn't happen.
Slog.wtf(TAG, "RemoteException", e);
@@ -2086,15 +2734,30 @@
}
}
+ /**
+ * Returns {@link PackageInfo} unless it's uninstalled or disabled.
+ */
+ @Nullable
@VisibleForTesting
- PackageInfo injectPackageInfo(String packageName, @UserIdInt int userId,
+ final PackageInfo getPackageInfo(String packageName, @UserIdInt int userId,
boolean getSignatures) {
- final long start = System.currentTimeMillis();
+ return isInstalledOrNull(injectPackageInfoWithUninstalled(
+ packageName, userId, getSignatures));
+ }
+
+ /**
+ * Do not use directly; this returns uninstalled packages too.
+ */
+ @Nullable
+ @VisibleForTesting
+ PackageInfo injectPackageInfoWithUninstalled(String packageName, @UserIdInt int userId,
+ boolean getSignatures) {
+ final long start = injectElapsedRealtime();
final long token = injectClearCallingIdentity();
try {
- return mIPackageManager.getPackageInfo(packageName, PACKAGE_MATCH_FLAGS
- | (getSignatures ? PackageManager.GET_SIGNATURES : 0)
- , userId);
+ return mIPackageManager.getPackageInfo(
+ packageName, PACKAGE_MATCH_FLAGS
+ | (getSignatures ? PackageManager.GET_SIGNATURES : 0), userId);
} catch (RemoteException e) {
// Shouldn't happen.
Slog.wtf(TAG, "RemoteException", e);
@@ -2108,9 +2771,23 @@
}
}
+ /**
+ * Returns {@link ApplicationInfo} unless it's uninstalled or disabled.
+ */
+ @Nullable
@VisibleForTesting
- ApplicationInfo injectApplicationInfo(String packageName, @UserIdInt int userId) {
- final long start = System.currentTimeMillis();
+ final ApplicationInfo getApplicationInfo(String packageName, @UserIdInt int userId) {
+ return isInstalledOrNull(injectApplicationInfoWithUninstalled(packageName, userId));
+ }
+
+ /**
+ * Do not use directly; this returns uninstalled packages too.
+ */
+ @Nullable
+ @VisibleForTesting
+ ApplicationInfo injectApplicationInfoWithUninstalled(
+ String packageName, @UserIdInt int userId) {
+ final long start = injectElapsedRealtime();
final long token = injectClearCallingIdentity();
try {
return mIPackageManager.getApplicationInfo(packageName, PACKAGE_MATCH_FLAGS, userId);
@@ -2125,24 +2802,277 @@
}
}
- private boolean isApplicationFlagSet(String packageName, int userId, int flags) {
- final ApplicationInfo ai = injectApplicationInfo(packageName, userId);
- return (ai != null) && ((ai.flags & flags) == flags);
- }
-
- boolean isPackageInstalled(String packageName, int userId) {
- return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_INSTALLED);
+ /**
+ * Returns {@link ActivityInfo} with its metadata unless it's uninstalled or disabled.
+ */
+ @Nullable
+ final ActivityInfo getActivityInfoWithMetadata(ComponentName activity, @UserIdInt int userId) {
+ return isInstalledOrNull(injectGetActivityInfoWithMetadataWithUninstalled(
+ activity, userId));
}
/**
- * @return the version code of the package, or -1 if the app is not installed.
+ * Do not use directly; this returns uninstalled packages too.
*/
- int getApplicationVersionCode(String packageName, int userId) {
- final ApplicationInfo ai = injectApplicationInfo(packageName, userId);
- if ((ai == null) || ((ai.flags & ApplicationInfo.FLAG_INSTALLED) == 0)) {
- return -1;
+ @Nullable
+ @VisibleForTesting
+ ActivityInfo injectGetActivityInfoWithMetadataWithUninstalled(
+ ComponentName activity, @UserIdInt int userId) {
+ final long start = injectElapsedRealtime();
+ final long token = injectClearCallingIdentity();
+ try {
+ return mIPackageManager.getActivityInfo(activity,
+ (PACKAGE_MATCH_FLAGS | PackageManager.GET_META_DATA), userId);
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ Slog.wtf(TAG, "RemoteException", e);
+ return null;
+ } finally {
+ injectRestoreCallingIdentity(token);
+
+ logDurationStat(Stats.GET_ACTIVITY_WITH_METADATA, start);
}
- return ai.versionCode;
+ }
+
+ /**
+ * Return all installed and enabled packages.
+ */
+ @NonNull
+ @VisibleForTesting
+ final List<PackageInfo> getInstalledPackages(@UserIdInt int userId) {
+ final long start = injectElapsedRealtime();
+ final long token = injectClearCallingIdentity();
+ try {
+ final List<PackageInfo> all = injectGetPackagesWithUninstalled(userId);
+
+ all.removeIf(PACKAGE_NOT_INSTALLED);
+
+ return all;
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ Slog.wtf(TAG, "RemoteException", e);
+ return null;
+ } finally {
+ injectRestoreCallingIdentity(token);
+
+ logDurationStat(Stats.GET_INSTALLED_PACKAGES, start);
+ }
+ }
+
+ /**
+ * Do not use directly; this returns uninstalled packages too.
+ */
+ @NonNull
+ @VisibleForTesting
+ List<PackageInfo> injectGetPackagesWithUninstalled(@UserIdInt int userId)
+ throws RemoteException {
+ final ParceledListSlice<PackageInfo> parceledList =
+ mIPackageManager.getInstalledPackages(PACKAGE_MATCH_FLAGS, userId);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
+ }
+
+ private void forUpdatedPackages(@UserIdInt int userId, long lastScanTime,
+ Consumer<ApplicationInfo> callback) {
+ if (DEBUG) {
+ Slog.d(TAG, "forUpdatedPackages for user " + userId + ", lastScanTime=" + lastScanTime);
+ }
+ final List<PackageInfo> list = getInstalledPackages(userId);
+ for (int i = list.size() - 1; i >= 0; i--) {
+ final PackageInfo pi = list.get(i);
+
+ // If the package has been updated since the last scan time, then scan it.
+ // Also if it's a system app with no update, lastUpdateTime is not reliable, so
+ // just scan it.
+ if (pi.lastUpdateTime >= lastScanTime || isPureSystemApp(pi.applicationInfo)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Found updated package " + pi.packageName);
+ }
+ callback.accept(pi.applicationInfo);
+ }
+ }
+ }
+
+ /**
+ * @return true if it's a system app with no updates.
+ */
+ private boolean isPureSystemApp(ApplicationInfo ai) {
+ return ai.isSystemApp() && !ai.isUpdatedSystemApp();
+ }
+
+ private boolean isApplicationFlagSet(@NonNull String packageName, int userId, int flags) {
+ final ApplicationInfo ai = injectApplicationInfoWithUninstalled(packageName, userId);
+ return (ai != null) && ((ai.flags & flags) == flags);
+ }
+
+ private static boolean isInstalled(@Nullable ApplicationInfo ai) {
+ return (ai != null) && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
+ }
+
+ private static boolean isInstalled(@Nullable PackageInfo pi) {
+ return (pi != null) && isInstalled(pi.applicationInfo);
+ }
+
+ private static boolean isInstalled(@Nullable ActivityInfo ai) {
+ return (ai != null) && isInstalled(ai.applicationInfo);
+ }
+
+ private static ApplicationInfo isInstalledOrNull(ApplicationInfo ai) {
+ return isInstalled(ai) ? ai : null;
+ }
+
+ private static PackageInfo isInstalledOrNull(PackageInfo pi) {
+ return isInstalled(pi) ? pi : null;
+ }
+
+ private static ActivityInfo isInstalledOrNull(ActivityInfo ai) {
+ return isInstalled(ai) ? ai : null;
+ }
+
+ boolean isPackageInstalled(String packageName, int userId) {
+ return getApplicationInfo(packageName, userId) != null;
+ }
+
+ @Nullable
+ XmlResourceParser injectXmlMetaData(ActivityInfo activityInfo, String key) {
+ return activityInfo.loadXmlMetaData(mContext.getPackageManager(), key);
+ }
+
+ @Nullable
+ Resources injectGetResourcesForApplicationAsUser(String packageName, int userId) {
+ final long start = injectElapsedRealtime();
+ final long token = injectClearCallingIdentity();
+ try {
+ return mContext.getPackageManager().getResourcesForApplicationAsUser(
+ packageName, userId);
+ } catch (NameNotFoundException e) {
+ Slog.e(TAG, "Resources for package " + packageName + " not found");
+ return null;
+ } finally {
+ injectRestoreCallingIdentity(token);
+
+ logDurationStat(Stats.GET_APPLICATION_RESOURCES, start);
+ }
+ }
+
+ private Intent getMainActivityIntent() {
+ final Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(LAUNCHER_INTENT_CATEGORY);
+ return intent;
+ }
+
+ /**
+ * Same as queryIntentActivitiesAsUser, except it makes sure the package is installed,
+ * and only returns exported activities.
+ */
+ @NonNull
+ @VisibleForTesting
+ List<ResolveInfo> queryActivities(@NonNull Intent baseIntent,
+ @NonNull String packageName, @Nullable ComponentName activity, int userId) {
+
+ baseIntent.setPackage(Preconditions.checkNotNull(packageName));
+ if (activity != null) {
+ baseIntent.setComponent(activity);
+ }
+
+ final List<ResolveInfo> resolved =
+ mContext.getPackageManager().queryIntentActivitiesAsUser(
+ baseIntent, PACKAGE_MATCH_FLAGS, userId);
+ if (resolved == null || resolved.size() == 0) {
+ return EMPTY_RESOLVE_INFO;
+ }
+ // Make sure the package is installed.
+ if (!isInstalled(resolved.get(0).activityInfo)) {
+ return EMPTY_RESOLVE_INFO;
+ }
+ resolved.removeIf(ACTIVITY_NOT_EXPORTED);
+ return resolved;
+ }
+
+ /**
+ * Return the main activity that is enabled and exported. If multiple activities are found,
+ * return the first one.
+ */
+ @Nullable
+ ComponentName injectGetDefaultMainActivity(@NonNull String packageName, int userId) {
+ final long start = injectElapsedRealtime();
+ final long token = injectClearCallingIdentity();
+ try {
+ final List<ResolveInfo> resolved =
+ queryActivities(getMainActivityIntent(), packageName, null, userId);
+ return resolved.size() == 0 ? null : resolved.get(0).activityInfo.getComponentName();
+ } finally {
+ injectRestoreCallingIdentity(token);
+
+ logDurationStat(Stats.GET_LAUNCHER_ACTIVITY, start);
+ }
+ }
+
+ /**
+ * Return whether an activity is enabled, exported and main.
+ */
+ boolean injectIsMainActivity(@NonNull ComponentName activity, int userId) {
+ final long start = injectElapsedRealtime();
+ final long token = injectClearCallingIdentity();
+ try {
+ final List<ResolveInfo> resolved =
+ queryActivities(getMainActivityIntent(), activity.getPackageName(),
+ activity, userId);
+ return resolved.size() > 0;
+ } finally {
+ injectRestoreCallingIdentity(token);
+
+ logDurationStat(Stats.CHECK_LAUNCHER_ACTIVITY, start);
+ }
+ }
+
+ /**
+ * Return all the enabled, exported and main activities from a package.
+ */
+ @NonNull
+ List<ResolveInfo> injectGetMainActivities(@NonNull String packageName, int userId) {
+ final long start = injectElapsedRealtime();
+ final long token = injectClearCallingIdentity();
+ try {
+ return queryActivities(getMainActivityIntent(), packageName, null, userId);
+ } finally {
+ injectRestoreCallingIdentity(token);
+
+ logDurationStat(Stats.CHECK_LAUNCHER_ACTIVITY, start);
+ }
+ }
+
+ /**
+ * Return whether an activity is enabled and exported.
+ */
+ @VisibleForTesting
+ boolean injectIsActivityEnabledAndExported(
+ @NonNull ComponentName activity, @UserIdInt int userId) {
+ final long start = injectElapsedRealtime();
+ final long token = injectClearCallingIdentity();
+ try {
+ return queryActivities(new Intent(), activity.getPackageName(), activity, userId)
+ .size() > 0;
+ } finally {
+ injectRestoreCallingIdentity(token);
+
+ logDurationStat(Stats.IS_ACTIVITY_ENABLED, start);
+ }
+ }
+
+ boolean injectIsSafeModeEnabled() {
+ final long token = injectClearCallingIdentity();
+ try {
+ return IWindowManager.Stub
+ .asInterface(ServiceManager.getService(Context.WINDOW_SERVICE))
+ .isSafeModeEnabled();
+ } catch (RemoteException e) {
+ return false; // Shouldn't happen though.
+ } finally {
+ injectRestoreCallingIdentity(token);
+ }
}
// === Backup & restore ===
@@ -2162,19 +3092,24 @@
Slog.d(TAG, "Backing up user " + userId);
}
synchronized (mLock) {
- final ShortcutUser user = getUserShortcutsLocked(userId);
- if (user == null) {
- Slog.w(TAG, "Can't backup: user not found: id=" + userId);
+ if (!isUserUnlockedL(userId)) {
+ wtf("Can't backup: user " + userId + " is locked or not running");
return null;
}
- user.forAllPackageItems(spi -> spi.refreshPackageInfoAndSave(this));
+ final ShortcutUser user = getUserShortcutsLocked(userId);
+ if (user == null) {
+ wtf("Can't backup: user not found: id=" + userId);
+ return null;
+ }
+
+ user.forAllPackageItems(spi -> spi.refreshPackageInfoAndSave());
// Then save.
final ByteArrayOutputStream os = new ByteArrayOutputStream(32 * 1024);
try {
saveUserInternalLocked(userId, os, /* forBackup */ true);
- } catch (XmlPullParserException|IOException e) {
+ } catch (XmlPullParserException | IOException e) {
// Shouldn't happen.
Slog.w(TAG, "Backup failed.", e);
return null;
@@ -2189,23 +3124,25 @@
if (DEBUG) {
Slog.d(TAG, "Restoring user " + userId);
}
- final ShortcutUser user;
- final ByteArrayInputStream is = new ByteArrayInputStream(payload);
- try {
- user = loadUserInternal(userId, is, /* fromBackup */ true);
- } catch (XmlPullParserException|IOException e) {
- Slog.w(TAG, "Restoration failed.", e);
- return;
- }
synchronized (mLock) {
+ if (!isUserUnlockedL(userId)) {
+ wtf("Can't restore: user " + userId + " is locked or not running");
+ return;
+ }
+ final ShortcutUser user;
+ final ByteArrayInputStream is = new ByteArrayInputStream(payload);
+ try {
+ user = loadUserInternal(userId, is, /* fromBackup */ true);
+ } catch (XmlPullParserException | IOException e) {
+ Slog.w(TAG, "Restoration failed.", e);
+ return;
+ }
mUsers.put(userId, user);
- // Then purge all the save images.
- final File bitmapPath = getUserBitmapFilePath(userId);
- final boolean success = FileUtils.deleteContents(bitmapPath);
- if (!success) {
- Slog.w(TAG, "Failed to delete " + bitmapPath);
- }
+ // Rescan all packages to re-publish manifest shortcuts and do other checks.
+ rescanUpdatedPackagesLocked(userId,
+ 0, // lastScanTime = 0; rescan all packages.
+ /* forceRescan= */ true);
saveUserLocked(userId);
}
@@ -2215,20 +3152,29 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump UserManager from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + " without permission "
- + android.Manifest.permission.DUMP);
- return;
+ enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
+ "can't dump by this caller");
+ boolean checkin = false;
+ boolean clear = false;
+ if (args != null) {
+ for (String arg : args) {
+ if ("-c".equals(arg)) {
+ checkin = true;
+ } else if ("--checkin".equals(arg)) {
+ checkin = true;
+ clear = true;
+ }
+ }
}
- dumpInner(pw, args);
+
+ if (checkin) {
+ dumpCheckin(pw, clear);
+ } else {
+ dumpInner(pw);
+ }
}
- @VisibleForTesting
- void dumpInner(PrintWriter pw, String[] args) {
+ private void dumpInner(PrintWriter pw) {
synchronized (mLock) {
final long now = injectCurrentTimeMillis();
pw.print("Now: [");
@@ -2253,10 +3199,6 @@
pw.print("] ");
pw.print(formatTime(next));
- pw.print(" Locale change seq#: ");
- pw.print(mLocaleChangeSequenceNumber.get());
- pw.println();
-
pw.print(" Config:");
pw.print(" Max icon dim: ");
pw.println(mMaxIconDimension);
@@ -2270,8 +3212,8 @@
pw.println(mResetInterval);
pw.print(" maxUpdatesPerInterval: ");
pw.println(mMaxUpdatesPerInterval);
- pw.print(" maxDynamicShortcuts: ");
- pw.println(mMaxDynamicShortcuts);
+ pw.print(" maxShortcutsPerActivity: ");
+ pw.println(mMaxShortcuts);
pw.println();
pw.println(" Stats:");
@@ -2283,11 +3225,30 @@
dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO, "getPackageInfo()");
dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO_WITH_SIG, "getPackageInfo(SIG)");
dumpStatLS(pw, p, Stats.GET_APPLICATION_INFO, "getApplicationInfo");
+ dumpStatLS(pw, p, Stats.CLEANUP_DANGLING_BITMAPS, "cleanupDanglingBitmaps");
+ dumpStatLS(pw, p, Stats.GET_ACTIVITY_WITH_METADATA, "getActivity+metadata");
+ dumpStatLS(pw, p, Stats.GET_INSTALLED_PACKAGES, "getInstalledPackages");
+ dumpStatLS(pw, p, Stats.CHECK_PACKAGE_CHANGES, "checkPackageChanges");
+ dumpStatLS(pw, p, Stats.GET_APPLICATION_RESOURCES, "getApplicationResources");
+ dumpStatLS(pw, p, Stats.RESOURCE_NAME_LOOKUP, "resourceNameLookup");
+ dumpStatLS(pw, p, Stats.GET_LAUNCHER_ACTIVITY, "getLauncherActivity");
+ dumpStatLS(pw, p, Stats.CHECK_LAUNCHER_ACTIVITY, "checkLauncherActivity");
+ dumpStatLS(pw, p, Stats.IS_ACTIVITY_ENABLED, "isActivityEnabled");
+ dumpStatLS(pw, p, Stats.PACKAGE_UPDATE_CHECK, "packageUpdateCheck");
+ }
+
+ pw.println();
+ pw.print(" #Failures: ");
+ pw.println(mWtfCount);
+
+ if (mLastWtfStacktrace != null) {
+ pw.print(" Last failure stack trace: ");
+ pw.println(Log.getStackTraceString(mLastWtfStacktrace));
}
for (int i = 0; i < mUsers.size(); i++) {
pw.println();
- mUsers.valueAt(i).dump(this, pw, " ");
+ mUsers.valueAt(i).dump(pw, " ");
}
pw.println();
@@ -2325,6 +3286,34 @@
(count == 0 ? 0 : ((double) dur) / count)));
}
+ /**
+ * Dumpsys for checkin.
+ *
+ * @param clear if true, clear the history information. Some other system services have this
+ * behavior but shortcut service doesn't for now.
+ */
+ private void dumpCheckin(PrintWriter pw, boolean clear) {
+ synchronized (mLock) {
+ try {
+ final JSONArray users = new JSONArray();
+
+ for (int i = 0; i < mUsers.size(); i++) {
+ users.put(mUsers.valueAt(i).dumpCheckin(clear));
+ }
+
+ final JSONObject result = new JSONObject();
+
+ result.put(KEY_SHORTCUT, users);
+ result.put(KEY_LOW_RAM, injectIsLowRamDevice());
+ result.put(KEY_ICON_SIZE, mMaxIconDimension);
+
+ pw.println(result.toString(1));
+ } catch (JSONException e) {
+ Slog.e(TAG, "Unable to write in json", e);
+ }
+ }
+ }
+
// === Shell support ===
@Override
@@ -2333,7 +3322,13 @@
enforceShell();
- (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver);
+ final long token = injectClearCallingIdentity();
+ try {
+ final int status = (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver);
+ resultReceiver.send(status, null);
+ } finally {
+ injectRestoreCallingIdentity(token);
+ }
}
static class CommandException extends Exception {
@@ -2349,7 +3344,7 @@
private int mUserId = UserHandle.USER_SYSTEM;
- private void parseOptions(boolean takeUser)
+ private void parseOptionsLocked(boolean takeUser)
throws CommandException {
String opt;
while ((opt = getNextOption()) != null) {
@@ -2357,6 +3352,10 @@
case "--user":
if (takeUser) {
mUserId = UserHandle.parseUserArg(getNextArgRequired());
+ if (!isUserUnlockedL(mUserId)) {
+ throw new CommandException(
+ "User " + mUserId + " is not running or locked");
+ }
break;
}
// fallthrough
@@ -2374,9 +3373,6 @@
final PrintWriter pw = getOutPrintWriter();
try {
switch (cmd) {
- case "reset-package-throttling":
- handleResetPackageThrottling();
- break;
case "reset-throttling":
handleResetThrottling();
break;
@@ -2395,15 +3391,15 @@
case "get-default-launcher":
handleGetDefaultLauncher();
break;
- case "refresh-default-launcher":
- handleRefreshDefaultLauncher();
- break;
case "unload-user":
handleUnloadUser();
break;
case "clear-shortcuts":
handleClearShortcuts();
break;
+ case "verify-states": // hidden command to verify various internal states.
+ handleVerifyStates();
+ break;
default:
return handleDefaultCommands(cmd);
}
@@ -2420,9 +3416,6 @@
final PrintWriter pw = getOutPrintWriter();
pw.println("Usage: cmd shortcut COMMAND [options ...]");
pw.println();
- pw.println("cmd shortcut reset-package-throttling [--user USER_ID] PACKAGE");
- pw.println(" Reset throttling for a package");
- pw.println();
pw.println("cmd shortcut reset-throttling [--user USER_ID]");
pw.println(" Reset throttling for all packages and users");
pw.println();
@@ -2439,10 +3432,7 @@
pw.println(" Clear the cached default launcher");
pw.println();
pw.println("cmd shortcut get-default-launcher [--user USER_ID]");
- pw.println(" Show the cached default launcher");
- pw.println();
- pw.println("cmd shortcut refresh-default-launcher [--user USER_ID]");
- pw.println(" Refresh the cached default launcher");
+ pw.println(" Show the default launcher");
pw.println();
pw.println("cmd shortcut unload-user [--user USER_ID]");
pw.println(" Unload a user from the memory");
@@ -2454,11 +3444,13 @@
}
private void handleResetThrottling() throws CommandException {
- parseOptions(/* takeUser =*/ true);
+ synchronized (mLock) {
+ parseOptionsLocked(/* takeUser =*/ true);
- Slog.i(TAG, "cmd: handleResetThrottling");
+ Slog.i(TAG, "cmd: handleResetThrottling: user=" + mUserId);
- resetThrottlingInner(mUserId);
+ resetThrottlingInner(mUserId);
+ }
}
private void handleResetAllThrottling() {
@@ -2467,16 +3459,6 @@
resetAllThrottlingInner();
}
- private void handleResetPackageThrottling() throws CommandException {
- parseOptions(/* takeUser =*/ true);
-
- final String packageName = getNextArgRequired();
-
- Slog.i(TAG, "cmd: handleResetPackageThrottling: " + packageName);
-
- resetPackageThrottling(packageName, mUserId);
- }
-
private void handleOverrideConfig() throws CommandException {
final String config = getNextArgRequired();
@@ -2499,8 +3481,7 @@
private void clearLauncher() {
synchronized (mLock) {
- getUserShortcutsLocked(mUserId).setLauncherComponent(
- ShortcutService.this, null);
+ getUserShortcutsLocked(mUserId).forceClearLauncher();
}
}
@@ -2510,44 +3491,55 @@
hasShortcutHostPermissionInner("-", mUserId);
getOutPrintWriter().println("Launcher: "
- + getUserShortcutsLocked(mUserId).getLauncherComponent());
+ + getUserShortcutsLocked(mUserId).getLastKnownLauncher());
}
}
private void handleClearDefaultLauncher() throws CommandException {
- parseOptions(/* takeUser =*/ true);
+ synchronized (mLock) {
+ parseOptionsLocked(/* takeUser =*/ true);
- clearLauncher();
+ clearLauncher();
+ }
}
private void handleGetDefaultLauncher() throws CommandException {
- parseOptions(/* takeUser =*/ true);
+ synchronized (mLock) {
+ parseOptionsLocked(/* takeUser =*/ true);
- showLauncher();
- }
-
- private void handleRefreshDefaultLauncher() throws CommandException {
- parseOptions(/* takeUser =*/ true);
-
- clearLauncher();
- showLauncher();
+ clearLauncher();
+ showLauncher();
+ }
}
private void handleUnloadUser() throws CommandException {
- parseOptions(/* takeUser =*/ true);
+ synchronized (mLock) {
+ parseOptionsLocked(/* takeUser =*/ true);
- Slog.i(TAG, "cmd: handleUnloadUser: " + mUserId);
+ Slog.i(TAG, "cmd: handleUnloadUser: user=" + mUserId);
- ShortcutService.this.handleCleanupUser(mUserId);
+ ShortcutService.this.handleCleanupUser(mUserId);
+ }
}
private void handleClearShortcuts() throws CommandException {
- parseOptions(/* takeUser =*/ true);
- final String packageName = getNextArgRequired();
+ synchronized (mLock) {
+ parseOptionsLocked(/* takeUser =*/ true);
+ final String packageName = getNextArgRequired();
- Slog.i(TAG, "cmd: handleClearShortcuts: " + mUserId + ", " + packageName);
+ Slog.i(TAG, "cmd: handleClearShortcuts: user" + mUserId + ", " + packageName);
- ShortcutService.this.cleanUpPackageForAllLoadedUsers(packageName, mUserId);
+ ShortcutService.this.cleanUpPackageForAllLoadedUsers(packageName, mUserId,
+ /* appStillExists = */ true);
+ }
+ }
+
+ private void handleVerifyStates() throws CommandException {
+ try {
+ verifyStatesForce(); // This will throw when there's an issue.
+ } catch (Throwable th) {
+ throw new CommandException(th.getMessage() + "\n" + Log.getStackTraceString(th));
+ }
}
}
@@ -2587,11 +3579,18 @@
}
final void wtf(String message) {
- wtf( message, /* exception= */ null);
+ wtf(message, /* exception= */ null);
}
// Injection point.
- void wtf(String message, Exception e) {
+ void wtf(String message, Throwable e) {
+ if (e == null) {
+ e = new RuntimeException("Stacktrace");
+ }
+ synchronized (mLock) {
+ mWtfCount++;
+ mLastWtfStacktrace = new Exception("Last failure was logged here:");
+ }
Slog.wtf(TAG, message, e);
}
@@ -2618,11 +3617,6 @@
}
}
- @VisibleForTesting
- PackageManagerInternal injectPackageManagerInternal() {
- return mPackageManagerInternal;
- }
-
File getUserBitmapFilePath(@UserIdInt int userId) {
return new File(injectUserDataPath(userId), DIRECTORY_BITMAPS);
}
@@ -2633,8 +3627,8 @@
}
@VisibleForTesting
- int getMaxDynamicShortcutsForTest() {
- return mMaxDynamicShortcuts;
+ int getMaxShortcutsForTest() {
+ return mMaxShortcuts;
}
@VisibleForTesting
@@ -2663,15 +3657,51 @@
}
@VisibleForTesting
- ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) {
+ ShortcutPackage getPackageShortcutForTest(String packageName, int userId) {
synchronized (mLock) {
final ShortcutUser user = mUsers.get(userId);
if (user == null) return null;
- final ShortcutPackage pkg = user.getAllPackagesForTest().get(packageName);
+ return user.getAllPackagesForTest().get(packageName);
+ }
+ }
+
+ @VisibleForTesting
+ ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) {
+ synchronized (mLock) {
+ final ShortcutPackage pkg = getPackageShortcutForTest(packageName, userId);
if (pkg == null) return null;
return pkg.findShortcutById(shortcutId);
}
}
+
+ /**
+ * Control whether {@link #verifyStates} should be performed. We always perform it during unit
+ * tests.
+ */
+ @VisibleForTesting
+ boolean injectShouldPerformVerification() {
+ return DEBUG;
+ }
+
+ /**
+ * Check various internal states and throws if there's any inconsistency.
+ * This is normally only enabled during unit tests.
+ */
+ final void verifyStates() {
+ if (injectShouldPerformVerification()) {
+ verifyStatesInner();
+ }
+ }
+
+ private final void verifyStatesForce() {
+ verifyStatesInner();
+ }
+
+ private void verifyStatesInner() {
+ synchronized (mLock) {
+ forEachLoadedUserLocked(u -> u.forAllPackageItems(ShortcutPackageItem::verifyStates));
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 7d19a78..ce3ed9c 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -16,18 +16,25 @@
package com.android.server.pm;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ComponentName;
+import android.content.pm.ShortcutManager;
+import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import libcore.util.Objects;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -39,6 +46,8 @@
/**
* User information used by {@link ShortcutService}.
+ *
+ * All methods should be guarded by {@code #mService.mLock}.
*/
class ShortcutUser {
private static final String TAG = ShortcutService.TAG;
@@ -47,7 +56,13 @@
private static final String TAG_LAUNCHER = "launcher";
private static final String ATTR_VALUE = "value";
- private static final String ATTR_KNOWN_LOCALE_CHANGE_SEQUENCE_NUMBER = "locale-seq-no";
+ private static final String ATTR_KNOWN_LOCALES = "locales";
+
+ // Suffix "2" was added to force rescan all packages after the next OTA.
+ private static final String ATTR_LAST_APP_SCAN_TIME = "last-app-scan-time2";
+ private static final String KEY_USER_ID = "userId";
+ private static final String KEY_LAUNCHERS = "launchers";
+ private static final String KEY_PACKAGES = "packages";
static final class PackageWithUser {
final int userId;
@@ -83,25 +98,35 @@
@Override
public String toString() {
- return String.format("{Package: %d, %s}", userId, packageName);
+ return String.format("[Package: %d, %s]", userId, packageName);
}
}
+ final ShortcutService mService;
+
@UserIdInt
private final int mUserId;
private final ArrayMap<String, ShortcutPackage> mPackages = new ArrayMap<>();
- private final SparseArray<ShortcutPackage> mPackagesFromUid = new SparseArray<>();
-
private final ArrayMap<PackageWithUser, ShortcutLauncher> mLaunchers = new ArrayMap<>();
- /** Default launcher that can access the launcher apps APIs. */
- private ComponentName mLauncherComponent;
+ /**
+ * Last known launcher. It's used when the default launcher isn't set in PM -- i.e.
+ * when getHomeActivitiesAsUser() return null. We need it so that in this situation the
+ * previously default launcher can still access shortcuts.
+ */
+ private ComponentName mLastKnownLauncher;
- private long mKnownLocaleChangeSequenceNumber;
+ /** In-memory-cached default launcher. */
+ private ComponentName mCachedLauncher;
- public ShortcutUser(int userId) {
+ private String mKnownLocales;
+
+ private long mLastAppScanTime;
+
+ public ShortcutUser(ShortcutService service, int userId) {
+ mService = service;
mUserId = userId;
}
@@ -109,6 +134,14 @@
return mUserId;
}
+ public long getLastAppScanTime() {
+ return mLastAppScanTime;
+ }
+
+ public void setLastAppScanTime(long lastAppScanTime) {
+ mLastAppScanTime = lastAppScanTime;
+ }
+
// We don't expose this directly to non-test code because only ShortcutUser should add to/
// remove from it.
@VisibleForTesting
@@ -116,10 +149,14 @@
return mPackages;
}
- public ShortcutPackage removePackage(@NonNull ShortcutService s, @NonNull String packageName) {
+ public boolean hasPackage(@NonNull String packageName) {
+ return mPackages.containsKey(packageName);
+ }
+
+ public ShortcutPackage removePackage(@NonNull String packageName) {
final ShortcutPackage removed = mPackages.remove(packageName);
- s.cleanupBitmapsForPackage(mUserId, packageName);
+ mService.cleanupBitmapsForPackage(mUserId, packageName);
return removed;
}
@@ -136,23 +173,33 @@
launcher.getPackageName()), launcher);
}
+ @Nullable
public ShortcutLauncher removeLauncher(
@UserIdInt int packageUserId, @NonNull String packageName) {
return mLaunchers.remove(PackageWithUser.of(packageUserId, packageName));
}
- public ShortcutPackage getPackageShortcuts(ShortcutService s, @NonNull String packageName) {
- ShortcutPackage ret = mPackages.get(packageName);
- if (ret == null) {
- ret = new ShortcutPackage(s, this, mUserId, packageName);
- mPackages.put(packageName, ret);
- } else {
- ret.attemptToRestoreIfNeededAndSave(s);
+ @Nullable
+ public ShortcutPackage getPackageShortcutsIfExists(@NonNull String packageName) {
+ final ShortcutPackage ret = mPackages.get(packageName);
+ if (ret != null) {
+ ret.attemptToRestoreIfNeededAndSave();
}
return ret;
}
- public ShortcutLauncher getLauncherShortcuts(ShortcutService s, @NonNull String packageName,
+ @NonNull
+ public ShortcutPackage getPackageShortcuts(@NonNull String packageName) {
+ ShortcutPackage ret = getPackageShortcutsIfExists(packageName);
+ if (ret == null) {
+ ret = new ShortcutPackage(this, mUserId, packageName);
+ mPackages.put(packageName, ret);
+ }
+ return ret;
+ }
+
+ @NonNull
+ public ShortcutLauncher getLauncherShortcuts(@NonNull String packageName,
@UserIdInt int launcherUserId) {
final PackageWithUser key = PackageWithUser.of(launcherUserId, packageName);
ShortcutLauncher ret = mLaunchers.get(key);
@@ -160,7 +207,7 @@
ret = new ShortcutLauncher(this, mUserId, packageName, launcherUserId);
mLaunchers.put(key, ret);
} else {
- ret.attemptToRestoreIfNeededAndSave(s);
+ ret.attemptToRestoreIfNeededAndSave();
}
return ret;
}
@@ -195,72 +242,106 @@
}
/**
- * Reset all throttling counters for all packages, if there has been a system locale change.
+ * Must be called at any entry points on {@link ShortcutManager} APIs to make sure the
+ * information on the package is up-to-date.
+ *
+ * We use broadcasts to handle locale changes and package changes, but because broadcasts
+ * are asynchronous, there's a chance a publisher calls getXxxShortcuts() after a certain event
+ * (e.g. system locale change) but shortcut manager hasn't finished processing the broadcast.
+ *
+ * So we call this method at all entry points from publishers to make sure we update all
+ * relevant information.
+ *
+ * Similar inconsistencies can happen when the launcher fetches shortcut information, but
+ * that's a less of an issue because for the launcher we report shortcut changes with
+ * callbacks.
*/
- public void resetThrottlingIfNeeded(ShortcutService s) {
- final long currentNo = s.getLocaleChangeSequenceNumber();
- if (mKnownLocaleChangeSequenceNumber < currentNo) {
- if (ShortcutService.DEBUG) {
- Slog.d(TAG, "LocaleChange detected for user " + mUserId);
- }
+ public void onCalledByPublisher(@NonNull String packageName) {
+ detectLocaleChange();
+ rescanPackageIfNeeded(packageName, /*forceRescan=*/ false);
+ }
- mKnownLocaleChangeSequenceNumber = currentNo;
-
- forAllPackages(p -> p.resetRateLimiting(s));
-
- s.scheduleSaveUser(mUserId);
+ private String getKnownLocales() {
+ if (TextUtils.isEmpty(mKnownLocales)) {
+ mKnownLocales = mService.injectGetLocaleTagsForUser(mUserId);
+ mService.scheduleSaveUser(mUserId);
}
+ return mKnownLocales;
}
/**
- * Called when a package is updated.
+ * Check to see if the system locale has changed, and if so, reset throttling
+ * and update resource strings.
*/
- public void handlePackageUpdated(ShortcutService s, @NonNull String packageName,
- int newVersionCode) {
- if (!mPackages.containsKey(packageName)) {
+ public void detectLocaleChange() {
+ final String currentLocales = mService.injectGetLocaleTagsForUser(mUserId);
+ if (getKnownLocales().equals(currentLocales)) {
return;
}
- getPackageShortcuts(s, packageName).handlePackageUpdated(s, newVersionCode);
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, "Locale changed from " + currentLocales + " to " + mKnownLocales
+ + " for user " + mUserId);
+ }
+ mKnownLocales = currentLocales;
+
+ forAllPackages(pkg -> {
+ pkg.resetRateLimiting();
+ pkg.resolveResourceStrings();
+ });
+
+ mService.scheduleSaveUser(mUserId);
+ }
+
+ public void rescanPackageIfNeeded(@NonNull String packageName, boolean forceRescan) {
+ final boolean isNewApp = !mPackages.containsKey(packageName);
+
+ final ShortcutPackage shortcutPackage = getPackageShortcuts(packageName);
+
+ if (!shortcutPackage.rescanPackageIfNeeded(isNewApp, forceRescan)) {
+ if (isNewApp) {
+ mPackages.remove(packageName);
+ }
+ }
}
public void attemptToRestoreIfNeededAndSave(ShortcutService s, @NonNull String packageName,
@UserIdInt int packageUserId) {
forPackageItem(packageName, packageUserId, spi -> {
- spi.attemptToRestoreIfNeededAndSave(s);
+ spi.attemptToRestoreIfNeededAndSave();
});
}
- public void saveToXml(ShortcutService s, XmlSerializer out, boolean forBackup)
+ public void saveToXml(XmlSerializer out, boolean forBackup)
throws IOException, XmlPullParserException {
out.startTag(null, TAG_ROOT);
- ShortcutService.writeAttr(out, ATTR_KNOWN_LOCALE_CHANGE_SEQUENCE_NUMBER,
- mKnownLocaleChangeSequenceNumber);
+ ShortcutService.writeAttr(out, ATTR_KNOWN_LOCALES, mKnownLocales);
+ ShortcutService.writeAttr(out, ATTR_LAST_APP_SCAN_TIME,
+ mLastAppScanTime);
- ShortcutService.writeTagValue(out, TAG_LAUNCHER,
- mLauncherComponent);
+ ShortcutService.writeTagValue(out, TAG_LAUNCHER, mLastKnownLauncher);
// Can't use forEachPackageItem due to the checked exceptions.
{
final int size = mLaunchers.size();
for (int i = 0; i < size; i++) {
- saveShortcutPackageItem(s, out, mLaunchers.valueAt(i), forBackup);
+ saveShortcutPackageItem(out, mLaunchers.valueAt(i), forBackup);
}
}
{
final int size = mPackages.size();
for (int i = 0; i < size; i++) {
- saveShortcutPackageItem(s, out, mPackages.valueAt(i), forBackup);
+ saveShortcutPackageItem(out, mPackages.valueAt(i), forBackup);
}
}
out.endTag(null, TAG_ROOT);
}
- private void saveShortcutPackageItem(ShortcutService s, XmlSerializer out,
+ private void saveShortcutPackageItem(XmlSerializer out,
ShortcutPackageItem spi, boolean forBackup) throws IOException, XmlPullParserException {
if (forBackup) {
- if (!s.shouldBackupApp(spi.getPackageName(), spi.getPackageUserId())) {
+ if (!mService.shouldBackupApp(spi.getPackageName(), spi.getPackageUserId())) {
return; // Don't save.
}
if (spi.getPackageUserId() != spi.getOwnerUserId()) {
@@ -272,10 +353,17 @@
public static ShortcutUser loadFromXml(ShortcutService s, XmlPullParser parser, int userId,
boolean fromBackup) throws IOException, XmlPullParserException {
- final ShortcutUser ret = new ShortcutUser(userId);
+ final ShortcutUser ret = new ShortcutUser(s, userId);
- ret.mKnownLocaleChangeSequenceNumber = ShortcutService.parseLongAttribute(parser,
- ATTR_KNOWN_LOCALE_CHANGE_SEQUENCE_NUMBER);
+ ret.mKnownLocales = ShortcutService.parseStringAttribute(parser,
+ ATTR_KNOWN_LOCALES);
+
+ // If lastAppScanTime is in the future, that means the clock went backwards.
+ // Just scan all apps again.
+ final long lastAppScanTime = ShortcutService.parseLongAttribute(parser,
+ ATTR_LAST_APP_SCAN_TIME);
+ final long currentTime = s.injectCurrentTimeMillis();
+ ret.mLastAppScanTime = lastAppScanTime < currentTime ? lastAppScanTime : 0;
final int outerDepth = parser.getDepth();
int type;
@@ -290,7 +378,7 @@
if (depth == outerDepth + 1) {
switch (tag) {
case TAG_LAUNCHER: {
- ret.mLauncherComponent = ShortcutService.parseComponentNameAttribute(
+ ret.mLastKnownLauncher = ShortcutService.parseComponentNameAttribute(
parser, ATTR_VALUE);
continue;
}
@@ -315,16 +403,42 @@
return ret;
}
- public ComponentName getLauncherComponent() {
- return mLauncherComponent;
+ public ComponentName getLastKnownLauncher() {
+ return mLastKnownLauncher;
}
- public void setLauncherComponent(ShortcutService s, ComponentName launcherComponent) {
- if (Objects.equal(mLauncherComponent, launcherComponent)) {
+ public void setLauncher(ComponentName launcherComponent) {
+ setLauncher(launcherComponent, /* allowPurgeLastKnown */ false);
+ }
+
+ /** Clears the launcher information without clearing the last known one */
+ public void clearLauncher() {
+ setLauncher(null);
+ }
+
+ /**
+ * Clears the launcher information *with(* clearing the last known one; we do this witl
+ * "cmd shortcut clear-default-launcher".
+ */
+ public void forceClearLauncher() {
+ setLauncher(null, /* allowPurgeLastKnown */ true);
+ }
+
+ private void setLauncher(ComponentName launcherComponent, boolean allowPurgeLastKnown) {
+ mCachedLauncher = launcherComponent; // Always update the in-memory cache.
+
+ if (Objects.equal(mLastKnownLauncher, launcherComponent)) {
return;
}
- mLauncherComponent = launcherComponent;
- s.scheduleSaveUser(mUserId);
+ if (!allowPurgeLastKnown && launcherComponent == null) {
+ return;
+ }
+ mLastKnownLauncher = launcherComponent;
+ mService.scheduleSaveUser(mUserId);
+ }
+
+ public ComponentName getCachedLauncher() {
+ return mCachedLauncher;
}
public void resetThrottling() {
@@ -333,36 +447,45 @@
}
}
- public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
pw.print(prefix);
pw.print("User: ");
pw.print(mUserId);
- pw.print(" Known locale seq#: ");
- pw.print(mKnownLocaleChangeSequenceNumber);
+ pw.print(" Known locales: ");
+ pw.print(mKnownLocales);
+ pw.print(" Last app scan: [");
+ pw.print(mLastAppScanTime);
+ pw.print("] ");
+ pw.print(ShortcutService.formatTime(mLastAppScanTime));
pw.println();
prefix += prefix + " ";
pw.print(prefix);
- pw.print("Default launcher: ");
- pw.print(mLauncherComponent);
+ pw.print("Cached launcher: ");
+ pw.print(mCachedLauncher);
+ pw.println();
+
+ pw.print(prefix);
+ pw.print("Last known launcher: ");
+ pw.print(mLastKnownLauncher);
pw.println();
for (int i = 0; i < mLaunchers.size(); i++) {
- mLaunchers.valueAt(i).dump(s, pw, prefix);
+ mLaunchers.valueAt(i).dump(pw, prefix);
}
for (int i = 0; i < mPackages.size(); i++) {
- mPackages.valueAt(i).dump(s, pw, prefix);
+ mPackages.valueAt(i).dump(pw, prefix);
}
pw.println();
pw.print(prefix);
pw.println("Bitmap directories: ");
- dumpDirectorySize(s, pw, prefix + " ", s.getUserBitmapFilePath(mUserId));
+ dumpDirectorySize(pw, prefix + " ", mService.getUserBitmapFilePath(mUserId));
}
- private void dumpDirectorySize(@NonNull ShortcutService s, @NonNull PrintWriter pw,
+ private void dumpDirectorySize(@NonNull PrintWriter pw,
@NonNull String prefix, File path) {
int numFiles = 0;
long size = 0;
@@ -373,7 +496,7 @@
numFiles++;
size += child.length();
} else if (child.isDirectory()) {
- dumpDirectorySize(s, pw, prefix + " ", child);
+ dumpDirectorySize(pw, prefix + " ", child);
}
}
}
@@ -385,7 +508,31 @@
pw.print(" files, size=");
pw.print(size);
pw.print(" (");
- pw.print(Formatter.formatFileSize(s.mContext, size));
+ pw.print(Formatter.formatFileSize(mService.mContext, size));
pw.println(")");
}
+
+ public JSONObject dumpCheckin(boolean clear) throws JSONException {
+ final JSONObject result = new JSONObject();
+
+ result.put(KEY_USER_ID, mUserId);
+
+ {
+ final JSONArray launchers = new JSONArray();
+ for (int i = 0; i < mLaunchers.size(); i++) {
+ launchers.put(mLaunchers.valueAt(i).dumpCheckin(clear));
+ }
+ result.put(KEY_LAUNCHERS, launchers);
+ }
+
+ {
+ final JSONArray packages = new JSONArray();
+ for (int i = 0; i < mPackages.size(); i++) {
+ packages.put(mPackages.valueAt(i).dumpCheckin(clear));
+ }
+ result.put(KEY_PACKAGES, packages);
+ }
+
+ return result;
+ }
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 632a98e..c9ad49a 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -27,15 +27,18 @@
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerNative;
+import android.app.AppGlobals;
import android.app.IActivityManager;
import android.app.IStopUserCallback;
import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
@@ -70,6 +73,7 @@
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
+import android.text.TextUtils;
import android.util.AtomicFile;
import android.util.IntArray;
import android.util.Log;
@@ -452,6 +456,7 @@
// user restriction was not a default guest restriction.
setUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, true, currentGuestUser.id);
}
+
mContext.registerReceiver(mDisableQuietModeCallback,
new IntentFilter(ACTION_DISABLE_QUIET_MODE_AFTER_UNLOCK),
null, mHandler);
@@ -466,6 +471,8 @@
UserInfo ui = mUsers.valueAt(i).info;
if ((ui.partial || ui.guestToRemove || ui.isEphemeral()) && i != 0) {
partials.add(ui);
+ mRemovingUserIds.append(ui.id, true);
+ ui.partial = true;
}
}
}
@@ -862,12 +869,25 @@
}
}
synchronized (mUsersLock) {
- UserInfo userInfo = getUserInfoLU(userId);
+ UserInfo userInfo = getUserInfoLU(userId);
return userInfo != null && userInfo.isManagedProfile();
}
}
@Override
+ public boolean isDemoUser(int userId) {
+ int callingUserId = UserHandle.getCallingUserId();
+ if (callingUserId != userId && !hasManageUsersPermission()) {
+ throw new SecurityException("You need MANAGE_USERS permission to query if u=" + userId
+ + " is a demo user");
+ }
+ synchronized (mUsersLock) {
+ UserInfo userInfo = getUserInfoLU(userId);
+ return userInfo != null && userInfo.isDemo();
+ }
+ }
+
+ @Override
public boolean isRestricted() {
synchronized (mUsersLock) {
return getUserInfoLU(UserHandle.getCallingUserId()).isRestricted();
@@ -1779,6 +1799,18 @@
mUserVersion = USER_VERSION;
Bundle restrictions = new Bundle();
+ try {
+ final String[] defaultFirstUserRestrictions = mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_defaultFirstUserRestrictions);
+ for (String userRestriction : defaultFirstUserRestrictions) {
+ if (UserRestrictionsUtils.isValidRestriction(userRestriction)) {
+ restrictions.putBoolean(userRestriction, true);
+ }
+ }
+ } catch (Resources.NotFoundException e) {
+ Log.e(LOG_TAG, "Couldn't find resource: config_defaultFirstUserRestrictions", e);
+ }
+
synchronized (mRestrictionsLock) {
mBaseUserRestrictions.append(UserHandle.USER_SYSTEM, restrictions);
}
@@ -2162,6 +2194,7 @@
final boolean isGuest = (flags & UserInfo.FLAG_GUEST) != 0;
final boolean isManagedProfile = (flags & UserInfo.FLAG_MANAGED_PROFILE) != 0;
final boolean isRestricted = (flags & UserInfo.FLAG_RESTRICTED) != 0;
+ final boolean isDemo = (flags & UserInfo.FLAG_DEMO) != 0;
final long ident = Binder.clearCallingIdentity();
UserInfo userInfo;
UserData userData;
@@ -2179,8 +2212,8 @@
Log.e(LOG_TAG, "Cannot add more managed profiles for user " + parentId);
return null;
}
- if (!isGuest && !isManagedProfile && isUserLimitReached()) {
- // If we're not adding a guest user or a managed profile and the limit has
+ if (!isGuest && !isManagedProfile && !isDemo && isUserLimitReached()) {
+ // If we're not adding a guest/demo user or a managed profile and the limit has
// been reached, cannot add a user.
return null;
}
@@ -2206,7 +2239,8 @@
return null;
}
}
- if (!UserManager.isSplitSystemUser() && (flags & UserInfo.FLAG_EPHEMERAL) != 0) {
+ if (!UserManager.isSplitSystemUser() && (flags & UserInfo.FLAG_EPHEMERAL) != 0
+ && (flags & UserInfo.FLAG_DEMO) == 0) {
Log.e(LOG_TAG,
"Ephemeral users are supported on split-system-user systems only.");
return null;
@@ -2282,6 +2316,7 @@
synchronized (mRestrictionsLock) {
mBaseUserRestrictions.append(userId, restrictions);
}
+ mPm.onNewUserCreated(userId);
Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
@@ -2852,11 +2887,9 @@
synchronized (mRestrictionsLock) {
applyUserRestrictionsLR(userId);
}
- UserInfo userInfo = getUserInfoNoChecks(userId);
- if (userInfo != null && !userInfo.isInitialized()) {
- mPm.onBeforeUserStartUninitialized(userId);
- }
}
+
+ maybeInitializeDemoMode(userId);
}
/**
@@ -2871,6 +2904,7 @@
/**
* Make a note of the last started time of a user and do some cleanup.
+ * This is called with ActivityManagerService lock held.
* @param userId the user that was just foregrounded
*/
public void onUserLoggedIn(@UserIdInt int userId) {
@@ -2888,6 +2922,29 @@
scheduleWriteUser(userData);
}
+ private void maybeInitializeDemoMode(int userId) {
+ if (UserManager.isDeviceInDemoMode(mContext) && userId != UserHandle.USER_SYSTEM) {
+ String demoLauncher =
+ mContext.getResources().getString(
+ com.android.internal.R.string.config_demoModeLauncherComponent);
+ if (!TextUtils.isEmpty(demoLauncher)) {
+ ComponentName componentToEnable = ComponentName.unflattenFromString(demoLauncher);
+ String demoLauncherPkg = componentToEnable.getPackageName();
+ try {
+ final IPackageManager iPm = AppGlobals.getPackageManager();
+ iPm.setComponentEnabledSetting(componentToEnable,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, /* flags= */ 0,
+ /* userId= */ userId);
+ iPm.setApplicationEnabledSetting(demoLauncherPkg,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, /* flags= */ 0,
+ /* userId= */ userId, null);
+ } catch (RemoteException re) {
+ // Internal, shouldn't happen
+ }
+ }
+ }
+ }
+
/**
* Returns the next available user id, filling in any holes in the ids.
* TODO: May not be a good idea to recycle ids, in case it results in confusion
@@ -2919,6 +2976,14 @@
* number is mismatched.
*/
public static void enforceSerialNumber(File file, int serialNumber) throws IOException {
+ if (StorageManager.isFileEncryptedEmulatedOnly()) {
+ // When we're emulating FBE, the directory may have been chmod
+ // 000'ed, meaning we can't read the serial number to enforce it;
+ // instead of destroying the user, just log a warning.
+ Slog.w(LOG_TAG, "Device is emulating FBE; assuming current serial number is valid");
+ return;
+ }
+
final int foundSerial = getSerialNumber(file);
Slog.v(LOG_TAG, "Found " + file + " with serial number " + foundSerial);
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 1f1c6f8..3df13a9 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -26,13 +26,12 @@
import android.app.ActivityManagerNative;
import android.content.ContentResolver;
import android.content.Context;
-import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.service.persistentdata.PersistentDataBlockManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.util.Log;
@@ -103,7 +102,9 @@
UserManager.DISALLOW_RUN_IN_BACKGROUND,
UserManager.DISALLOW_DATA_ROAMING,
UserManager.DISALLOW_SET_USER_ICON,
- UserManager.DISALLOW_SET_WALLPAPER
+ UserManager.DISALLOW_SET_WALLPAPER,
+ UserManager.DISALLOW_OEM_UNLOCK,
+ UserManager.DISALLLOW_UNMUTE_DEVICE,
});
/**
@@ -137,7 +138,8 @@
*/
private static final Set<String> IMMUTABLE_BY_OWNERS = Sets.newArraySet(
UserManager.DISALLOW_RECORD_AUDIO,
- UserManager.DISALLOW_WALLPAPER
+ UserManager.DISALLOW_WALLPAPER,
+ UserManager.DISALLOW_OEM_UNLOCK
);
/**
@@ -147,7 +149,8 @@
private static final Set<String> GLOBAL_RESTRICTIONS = Sets.newArraySet(
UserManager.DISALLOW_ADJUST_VOLUME,
UserManager.DISALLOW_RUN_IN_BACKGROUND,
- UserManager.DISALLOW_UNMUTE_MICROPHONE
+ UserManager.DISALLOW_UNMUTE_MICROPHONE,
+ UserManager.DISALLLOW_UNMUTE_DEVICE
);
/**
@@ -183,8 +186,7 @@
serializer.endTag(null, tag);
}
- public static void readRestrictions(XmlPullParser parser, Bundle restrictions)
- throws IOException {
+ public static void readRestrictions(XmlPullParser parser, Bundle restrictions) {
for (String key : USER_RESTRICTIONS) {
final String value = parser.getAttributeValue(null, key);
if (value != null) {
@@ -427,6 +429,22 @@
android.provider.Settings.Global.SAFE_BOOT_DISALLOWED,
newValue ? 1 : 0);
break;
+ case UserManager.DISALLOW_FACTORY_RESET:
+ case UserManager.DISALLOW_OEM_UNLOCK:
+ if (newValue) {
+ PersistentDataBlockManager manager = (PersistentDataBlockManager) context
+ .getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
+ if (manager != null
+ && manager.getOemUnlockEnabled()
+ && manager.getFlashLockState()
+ != PersistentDataBlockManager.FLASH_LOCK_UNLOCKED) {
+ // Only disable OEM unlock if the bootloader is locked. If it's already
+ // unlocked, setting the OEM unlock enabled flag to false has no effect
+ // (the bootloader would remain unlocked).
+ manager.setOemUnlockEnabled(false);
+ }
+ }
+ break;
}
} finally {
Binder.restoreCallingIdentity(id);
diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java
index 5ef518e..bb91f76 100644
--- a/services/core/java/com/android/server/policy/GlobalActions.java
+++ b/services/core/java/com/android/server/policy/GlobalActions.java
@@ -103,6 +103,7 @@
private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";
private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
+ private static final String GLOBAL_ACTION_KEY_RESTART = "restart";
private final Context mContext;
private final WindowManagerFuncs mWindowManagerFuncs;
@@ -298,6 +299,8 @@
mItems.add(getVoiceAssistAction());
} else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
mItems.add(getAssistAction());
+ } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
+ mItems.add(new RestartAction());
} else {
Log.e(TAG, "Invalid global action key " + actionKey);
}
@@ -369,6 +372,38 @@
}
}
+ private final class RestartAction extends SinglePressAction implements LongPressAction {
+ private RestartAction() {
+ super(R.drawable.ic_restart, R.string.global_action_restart);
+ }
+
+ @Override
+ public boolean onLongPress() {
+ UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
+ mWindowManagerFuncs.rebootSafeMode(true);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean showDuringKeyguard() {
+ return true;
+ }
+
+ @Override
+ public boolean showBeforeProvisioning() {
+ return true;
+ }
+
+ @Override
+ public void onPress() {
+ mWindowManagerFuncs.reboot(false /* confirm */);
+ }
+ }
+
+
private class BugReportAction extends SinglePressAction implements LongPressAction {
public BugReportAction() {
@@ -1145,7 +1180,7 @@
public GlobalActionsDialog(Context context, AlertParams params) {
super(context, getDialogTheme(context));
mContext = getContext();
- mAlert = new AlertController(mContext, this, getWindow());
+ mAlert = AlertController.create(mContext, this, getWindow());
mAdapter = (MyAdapter) params.mAdapter;
mWindowTouchSlop = ViewConfiguration.get(context).getScaledWindowTouchSlop();
params.apply(mAlert);
diff --git a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
index a77d512..c764833 100644
--- a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
@@ -30,6 +30,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
@@ -133,7 +134,7 @@
}
public void immersiveModeChangedLw(String pkg, boolean isImmersiveMode,
- boolean userSetupComplete) {
+ boolean userSetupComplete, boolean navBarEmpty) {
mHandler.removeMessages(H.SHOW);
if (isImmersiveMode) {
final boolean disabled = PolicyControl.disableImmersiveConfirmation(pkg);
@@ -142,7 +143,9 @@
if (!disabled
&& (DEBUG_SHOW_EVERY_TIME || !mConfirmed)
&& userSetupComplete
- && !mVrModeEnabled) {
+ && !mVrModeEnabled
+ && !navBarEmpty
+ && !UserManager.isDeviceInDemoMode(mContext)) {
mHandler.sendEmptyMessageDelayed(H.SHOW, mShowDelayMs);
}
} else {
@@ -150,12 +153,13 @@
}
}
- public boolean onPowerKeyDown(boolean isScreenOn, long time, boolean inImmersiveMode) {
+ public boolean onPowerKeyDown(boolean isScreenOn, long time, boolean inImmersiveMode,
+ boolean navBarEmpty) {
if (!isScreenOn && (time - mPanicTime < mPanicThresholdMs)) {
// turning the screen back on within the panic threshold
return mClingWindow == null;
}
- if (isScreenOn && inImmersiveMode) {
+ if (isScreenOn && inImmersiveMode && !navBarEmpty) {
// turning the screen off, remember if we were in immersive mode
mPanicTime = time;
} else {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a4408fc..a39add8 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -216,6 +216,8 @@
static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP = 0;
static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME = 1;
+ static final int PENDING_KEY_NULL = -1;
+
// Controls navigation bar opacity depending on which workspace stacks are currently
// visible.
// Nav bar is always opaque when either the freeform stack or docked stack is visible.
@@ -262,6 +264,10 @@
private static final String SYSUI_SCREENSHOT_ERROR_RECEIVER =
"com.android.systemui.screenshot.ScreenshotServiceErrorReceiver";
+ private static final int NAV_BAR_BOTTOM = 0;
+ private static final int NAV_BAR_RIGHT = 1;
+ private static final int NAV_BAR_LEFT = 2;
+
/**
* Keyguard stuff
*/
@@ -354,9 +360,8 @@
int mStatusBarHeight;
WindowState mNavigationBar = null;
boolean mHasNavigationBar = false;
- boolean mCanHideNavigationBar = false;
boolean mNavigationBarCanMove = false; // can the navigation bar ever move to the side?
- boolean mNavigationBarOnBottom = true; // is the navigation bar on the bottom *right now*?
+ int mNavigationBarPosition = NAV_BAR_BOTTOM;
int[] mNavigationBarHeightForRotationDefault = new int[4];
int[] mNavigationBarWidthForRotationDefault = new int[4];
int[] mNavigationBarHeightForRotationInCarMode = new int[4];
@@ -407,6 +412,10 @@
volatile boolean mRecentsVisible;
volatile boolean mTvPictureInPictureVisible;
+ // Used to hold the last user key used to wake the device. This helps us prevent up events
+ // from being passed to the foregrounded app without a corresponding down event
+ volatile int mPendingWakeKey = PENDING_KEY_NULL;
+
int mRecentAppsHeldModifiers;
boolean mLanguageSwitchKeyPressed;
@@ -1015,7 +1024,8 @@
// Detect user pressing the power button in panic when an application has
// taken over the whole screen.
boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(interactive,
- SystemClock.elapsedRealtime(), isImmersiveMode(mLastSystemUiFlags));
+ SystemClock.elapsedRealtime(), isImmersiveMode(mLastSystemUiFlags),
+ isNavBarEmpty(mLastSystemUiFlags));
if (panic) {
mHandler.post(mHiddenNavPanic);
}
@@ -1683,13 +1693,19 @@
}
@Override
public void onSwipeFromBottom() {
- if (mNavigationBar != null && mNavigationBarOnBottom) {
+ if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_BOTTOM) {
requestTransientBars(mNavigationBar);
}
}
@Override
public void onSwipeFromRight() {
- if (mNavigationBar != null && !mNavigationBarOnBottom) {
+ if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_RIGHT) {
+ requestTransientBars(mNavigationBar);
+ }
+ }
+ @Override
+ public void onSwipeFromLeft() {
+ if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_LEFT) {
requestTransientBars(mNavigationBar);
}
}
@@ -2213,6 +2229,9 @@
attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
}
break;
+ case TYPE_SCREENSHOT:
+ attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ break;
}
if (attrs.type != TYPE_STATUS_BAR) {
@@ -2782,8 +2801,8 @@
if (win.getAttrs().windowAnimations != 0) {
return 0;
}
- // This can be on either the bottom or the right.
- if (mNavigationBarOnBottom) {
+ // This can be on either the bottom or the right or the left.
+ if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
if (transit == TRANSIT_EXIT
|| transit == TRANSIT_HIDE) {
return R.anim.dock_bottom_exit;
@@ -2791,7 +2810,7 @@
|| transit == TRANSIT_SHOW) {
return R.anim.dock_bottom_enter;
}
- } else {
+ } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
if (transit == TRANSIT_EXIT
|| transit == TRANSIT_HIDE) {
return R.anim.dock_right_exit;
@@ -2799,6 +2818,14 @@
|| transit == TRANSIT_SHOW) {
return R.anim.dock_right_enter;
}
+ } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
+ if (transit == TRANSIT_EXIT
+ || transit == TRANSIT_HIDE) {
+ return R.anim.dock_left_exit;
+ } else if (transit == TRANSIT_ENTER
+ || transit == TRANSIT_SHOW) {
+ return R.anim.dock_left_enter;
+ }
}
} else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) {
return selectDockedDividerAnimationLw(win, transit);
@@ -2827,10 +2854,12 @@
// If the divider is behind the navigation bar, don't animate.
final Rect frame = win.getFrameLw();
final boolean behindNavBar = mNavigationBar != null
- && ((mNavigationBarOnBottom
+ && ((mNavigationBarPosition == NAV_BAR_BOTTOM
&& frame.top + insets >= mNavigationBar.getFrameLw().top)
- || (!mNavigationBarOnBottom
- && frame.left + insets >= mNavigationBar.getFrameLw().left));
+ || (mNavigationBarPosition == NAV_BAR_RIGHT
+ && frame.left + insets >= mNavigationBar.getFrameLw().left)
+ || (mNavigationBarPosition == NAV_BAR_LEFT
+ && frame.right - insets <= mNavigationBar.getFrameLw().right));
final boolean landscape = frame.height() > frame.width();
final boolean offscreenLandscape = landscape && (frame.right - insets <= 0
|| frame.left + insets >= win.getDisplayFrameLw().right);
@@ -2855,9 +2884,14 @@
+ mTopFullscreenOpaqueWindowState + " rotationAnimation="
+ (mTopFullscreenOpaqueWindowState == null ?
"0" : mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation));
- if (mTopFullscreenOpaqueWindowState != null && mTopIsFullscreen) {
- switch (mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation) {
+ if (mTopFullscreenOpaqueWindowState != null) {
+ int animationHint = mTopFullscreenOpaqueWindowState.getRotationAnimationHint();
+ if (animationHint < 0 && mTopIsFullscreen) {
+ animationHint = mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation;
+ }
+ switch (animationHint) {
case ROTATION_ANIMATION_CROSSFADE:
+ case ROTATION_ANIMATION_SEAMLESS: // Crossfade is fallback for seamless.
anim[0] = R.anim.rotation_animation_xfade_exit;
anim[1] = R.anim.rotation_animation_enter;
break;
@@ -3031,15 +3065,6 @@
return -1;
}
- // If an incoming call is ringing, HOME is totally disabled.
- // (The user is already on the InCallUI at this point,
- // and his ONLY options are to answer or reject the call.)
- TelecomManager telecomManager = getTelecommService();
- if (telecomManager != null && telecomManager.isRinging()) {
- Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
- return -1;
- }
-
// Delay handling home if a double-tap is possible.
if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
@@ -3539,6 +3564,14 @@
}
}
+ @Override
+ public boolean canShowDismissingWindowWhileLockedLw() {
+ // If the keyguard is trusted, it will unlock without a challange. Therefore, windows with
+ // FLAG_DISMISS_KEYGUARD don't need to be force hidden, as they will unlock the phone right
+ // away anyways.
+ return mKeyguardDelegate != null && mKeyguardDelegate.isTrusted();
+ }
+
private void launchAssistLongPressAction() {
performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST);
@@ -4023,7 +4056,7 @@
navVisible |= !canHideNavigationBar();
boolean updateSysUiVisibility = layoutNavigationBar(displayWidth, displayHeight,
- displayRotation, uiMode, overscanRight, overscanBottom, dcf, navVisible, navTranslucent,
+ displayRotation, uiMode, overscanLeft, overscanRight, overscanBottom, dcf, navVisible, navTranslucent,
navAllowedHidden, statusBarExpandedNotKeyguard);
if (DEBUG_LAYOUT) Slog.i(TAG, String.format("mDock rect: (%d,%d - %d,%d)",
mDockLeft, mDockTop, mDockRight, mDockBottom));
@@ -4102,8 +4135,8 @@
}
private boolean layoutNavigationBar(int displayWidth, int displayHeight, int displayRotation,
- int uiMode, int overscanRight, int overscanBottom, Rect dcf, boolean navVisible,
- boolean navTranslucent, boolean navAllowedHidden,
+ int uiMode, int overscanLeft, int overscanRight, int overscanBottom, Rect dcf,
+ boolean navVisible, boolean navTranslucent, boolean navAllowedHidden,
boolean statusBarExpandedNotKeyguard) {
if (mNavigationBar != null) {
boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
@@ -4111,8 +4144,9 @@
// size. We need to do this directly, instead of relying on
// it to bubble up from the nav bar, because this needs to
// change atomically with screen rotations.
- mNavigationBarOnBottom = isNavigationBarOnBottom(displayWidth, displayHeight);
- if (mNavigationBarOnBottom) {
+ mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight,
+ displayRotation);
+ if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
// It's a system nav bar or a portrait screen; nav bar goes on bottom.
int top = displayHeight - overscanBottom
- getNavigationBarHeight(displayRotation, uiMode);
@@ -4138,7 +4172,7 @@
// we can tell the app that it is covered by it.
mSystemBottom = mTmpNavigationFrame.top;
}
- } else {
+ } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
// Landscape screen; nav bar goes to the right.
int left = displayWidth - overscanRight
- getNavigationBarWidth(displayRotation, uiMode);
@@ -4164,6 +4198,33 @@
// we can tell the app that it is covered by it.
mSystemRight = mTmpNavigationFrame.left;
}
+ } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
+ // Seascape screen; nav bar goes to the left.
+ int right = overscanLeft + getNavigationBarWidth(displayRotation, uiMode);
+ mTmpNavigationFrame.set(overscanLeft, 0, right, displayHeight);
+ mStableLeft = mStableFullscreenLeft = mTmpNavigationFrame.right;
+ if (transientNavBarShowing) {
+ mNavigationBarController.setBarShowingLw(true);
+ } else if (navVisible) {
+ mNavigationBarController.setBarShowingLw(true);
+ mDockLeft = mTmpNavigationFrame.right;
+ // TODO: not so sure about those:
+ mRestrictedScreenLeft = mRestrictedOverscanScreenLeft = mDockLeft;
+ mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
+ mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;
+ } else {
+ // We currently want to hide the navigation UI - unless we expanded the status
+ // bar.
+ mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
+ }
+ if (navVisible && !navTranslucent && !navAllowedHidden
+ && !mNavigationBar.isAnimatingLw()
+ && !mNavigationBarController.wasRecentlyTranslucent()) {
+ // If the nav bar is currently requested to be visible,
+ // and not in the process of animating on or off, then
+ // we can tell the app that it is covered by it.
+ mSystemLeft = mTmpNavigationFrame.right;
+ }
}
// Make sure the content and current rectangles are updated to
// account for the restrictions from the navigation bar.
@@ -4184,8 +4245,15 @@
return false;
}
- private boolean isNavigationBarOnBottom(int displayWidth, int displayHeight) {
- return !mNavigationBarCanMove || displayWidth < displayHeight;
+ private int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {
+ if (mNavigationBarCanMove && displayWidth > displayHeight) {
+ if (displayRotation == Surface.ROTATION_270) {
+ return NAV_BAR_LEFT;
+ } else {
+ return NAV_BAR_RIGHT;
+ }
+ }
+ return NAV_BAR_BOTTOM;
}
/** {@inheritDoc} */
@@ -4361,7 +4429,11 @@
if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) {
// The status bar forces the navigation bar while it's visible. Make sure the IME
// avoids the navigation bar in that case.
- pf.right = df.right = of.right = cf.right = vf.right = mStableRight;
+ if (mNavigationBarPosition == NAV_BAR_RIGHT) {
+ pf.right = df.right = of.right = cf.right = vf.right = mStableRight;
+ } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
+ pf.left = df.left = of.left = cf.left = vf.left = mStableLeft;
+ }
}
// IM dock windows always go to the bottom of the screen.
attrs.gravity = Gravity.BOTTOM;
@@ -4390,6 +4462,8 @@
} else {
vf.set(cf);
}
+ } else if (attrs.type == TYPE_WALLPAPER) {
+ layoutWallpaper(win, pf, df, of, cf);
} else if (win == mStatusBar) {
pf.left = df.left = of.left = mUnrestrictedScreenLeft;
pf.top = df.top = of.top = mUnrestrictedScreenTop;
@@ -4613,17 +4687,6 @@
+ mOverscanScreenWidth;
pf.bottom = df.bottom = of.bottom = cf.bottom = mOverscanScreenTop
+ mOverscanScreenHeight;
- } else if (attrs.type == TYPE_WALLPAPER) {
- // The wallpaper also has Real Ultimate Power, but we want to tell
- // it about the overscan area.
- pf.left = df.left = mOverscanScreenLeft;
- pf.top = df.top = mOverscanScreenTop;
- pf.right = df.right = mOverscanScreenLeft + mOverscanScreenWidth;
- pf.bottom = df.bottom = mOverscanScreenTop + mOverscanScreenHeight;
- of.left = cf.left = mUnrestrictedScreenLeft;
- of.top = cf.top = mUnrestrictedScreenTop;
- of.right = cf.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
- of.bottom = cf.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
} else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
&& attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
&& attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
@@ -4816,6 +4879,20 @@
}
}
+ private void layoutWallpaper(WindowState win, Rect pf, Rect df, Rect of, Rect cf) {
+
+ // The wallpaper also has Real Ultimate Power, but we want to tell
+ // it about the overscan area.
+ pf.left = df.left = mOverscanScreenLeft;
+ pf.top = df.top = mOverscanScreenTop;
+ pf.right = df.right = mOverscanScreenLeft + mOverscanScreenWidth;
+ pf.bottom = df.bottom = mOverscanScreenTop + mOverscanScreenHeight;
+ of.left = cf.left = mUnrestrictedScreenLeft;
+ of.top = cf.top = mUnrestrictedScreenTop;
+ of.right = cf.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
+ of.bottom = cf.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ }
+
private void offsetInputMethodWindowLw(WindowState win) {
int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
top += win.getGivenContentInsetsLw().top;
@@ -4995,9 +5072,26 @@
}
}
- // Keep track of the window if it's dimming but not necessarily fullscreen.
final boolean reallyVisible = win.isVisibleOrBehindKeyguardLw() && !win.isGoneForLayoutLw();
- if (mTopFullscreenOpaqueOrDimmingWindowState == null && reallyVisible
+
+ // Voice interaction overrides both top fullscreen and top docked.
+ if (reallyVisible && win.getAttrs().type == TYPE_VOICE_INTERACTION) {
+ if (mTopFullscreenOpaqueWindowState == null) {
+ mTopFullscreenOpaqueWindowState = win;
+ if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
+ mTopFullscreenOpaqueOrDimmingWindowState = win;
+ }
+ }
+ if (mTopDockedOpaqueWindowState == null) {
+ mTopDockedOpaqueWindowState = win;
+ if (mTopDockedOpaqueOrDimmingWindowState == null) {
+ mTopDockedOpaqueOrDimmingWindowState = win;
+ }
+ }
+ }
+
+ // Keep track of the window if it's dimming but not necessarily fullscreen.
+ if (mTopFullscreenOpaqueOrDimmingWindowState == null && reallyVisible
&& win.isDimming() && StackId.normallyFullscreenWindows(stackId)) {
mTopFullscreenOpaqueOrDimmingWindowState = win;
}
@@ -5174,7 +5268,10 @@
}
} else if (mDismissKeyguard != DISMISS_KEYGUARD_NONE) {
mKeyguardHidden = false;
- if (setKeyguardOccludedLw(false)) {
+ final boolean trusted = mKeyguardDelegate.isTrusted();
+ if (trusted) {
+ // No need to un-occlude keyguard - we'll dimiss it right away anyways.
+ } else if (setKeyguardOccludedLw(false)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT
| FINISH_LAYOUT_REDO_CONFIG
| FINISH_LAYOUT_REDO_WALLPAPER;
@@ -5184,7 +5281,7 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- mKeyguardDelegate.dismiss();
+ mKeyguardDelegate.dismiss(trusted /* allowWhileOccluded */);
}
});
}
@@ -5223,6 +5320,9 @@
mKeyguardOccluded = false;
mKeyguardDelegate.setOccluded(false);
mStatusBar.getAttrs().privateFlags |= PRIVATE_FLAG_KEYGUARD;
+ if (!mKeyguardDelegate.hasLockscreenWallpaper()) {
+ mStatusBar.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
+ }
return true;
} else if (!wasOccluded && isOccluded && showing) {
mKeyguardOccluded = true;
@@ -5476,12 +5576,24 @@
// key to the application.
result = ACTION_PASS_TO_USER;
isWakeKey = false;
- } else if (!interactive && shouldDispatchInputWhenNonInteractive()) {
+
+ if (interactive) {
+ // If the screen is awake, but the button pressed was the one that woke the device
+ // then don't pass it to the application
+ if (keyCode == mPendingWakeKey && !down) {
+ result = 0;
+ }
+ // Reset the pending key
+ mPendingWakeKey = PENDING_KEY_NULL;
+ }
+ } else if (!interactive && shouldDispatchInputWhenNonInteractive(event)) {
// If we're currently dozing with the screen on and the keyguard showing, pass the key
// to the application but preserve its wake key status to make sure we still move
// from dozing to fully interactive if we would normally go from off to fully
// interactive.
result = ACTION_PASS_TO_USER;
+ // Since we're dispatching the input, reset the pending key
+ mPendingWakeKey = PENDING_KEY_NULL;
} else {
// When the screen is off and the key is not injected, determine whether
// to wake the device but don't pass the key to the application.
@@ -5489,6 +5601,10 @@
if (isWakeKey && (!down || !isWakeKeyWhenScreenOff(keyCode))) {
isWakeKey = false;
}
+ // Cache the wake key on down event so we can also avoid sending the up event to the app
+ if (isWakeKey && down) {
+ mPendingWakeKey = keyCode;
+ }
}
// If the key would be handled globally, just return the result, don't worry about special
@@ -5655,6 +5771,18 @@
break;
}
+ case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN:
+ // fall through
+ case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP:
+ // fall through
+ case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT:
+ // fall through
+ case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT: {
+ result &= ~ACTION_PASS_TO_USER;
+ interceptSystemNavigationKey(event);
+ break;
+ }
+
case KeyEvent.KEYCODE_SLEEP: {
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false;
@@ -5776,6 +5904,23 @@
}
/**
+ * Handle statusbar expansion events.
+ * @param event
+ */
+ private void interceptSystemNavigationKey(KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_UP && areSystemNavigationKeysEnabled()) {
+ IStatusBarService sbar = getStatusBarService();
+ if (sbar != null) {
+ try {
+ sbar.handleSystemNavigationKey(event.getKeyCode());
+ } catch (RemoteException e1) {
+ // oops, no statusbar. Ignore event.
+ }
+ }
+ }
+ }
+
+ /**
* Returns true if the key can have global actions attached to it.
* We reserve all power management keys for the system since they require
* very careful handling.
@@ -5836,7 +5981,7 @@
}
}
- if (shouldDispatchInputWhenNonInteractive()) {
+ if (shouldDispatchInputWhenNonInteractive(null)) {
return ACTION_PASS_TO_USER;
}
@@ -5851,7 +5996,7 @@
return 0;
}
- private boolean shouldDispatchInputWhenNonInteractive() {
+ private boolean shouldDispatchInputWhenNonInteractive(KeyEvent event) {
final boolean displayOff = (mDisplay == null || mDisplay.getState() == Display.STATE_OFF);
if (displayOff && !mHasFeatureWatch) {
@@ -5863,6 +6008,14 @@
return true;
}
+ // Watches handle BACK specially
+ if (mHasFeatureWatch
+ && event != null
+ && (event.getKeyCode() == KeyEvent.KEYCODE_BACK
+ || event.getKeyCode() == KeyEvent.KEYCODE_STEM_PRIMARY)) {
+ return false;
+ }
+
// Send events to a dozing dream even if the screen is off since the dream
// is in control of the state of the screen.
IDreamManager dreamManager = getDreamManager();
@@ -6050,7 +6203,9 @@
return;
}
mPendingPanicGestureUptime = SystemClock.uptimeMillis();
- mNavigationBarController.showTransient();
+ if (!isNavBarEmpty(mLastSystemUiFlags)) {
+ mNavigationBarController.showTransient();
+ }
}
}
};
@@ -6062,7 +6217,8 @@
return;
}
boolean sb = mStatusBarController.checkShowTransientBarLw();
- boolean nb = mNavigationBarController.checkShowTransientBarLw();
+ boolean nb = mNavigationBarController.checkShowTransientBarLw()
+ && !isNavBarEmpty(mLastSystemUiFlags);
if (sb || nb) {
// Don't show status bar when swiping on already visible navigation bar
if (!nb && swipeTarget == mNavigationBar) {
@@ -6373,7 +6529,7 @@
@Override
public void run() {
// ask the keyguard to prompt the user to authenticate if necessary
- mKeyguardDelegate.dismiss();
+ mKeyguardDelegate.dismiss(false /* allowWhileOccluded */);
}
});
}
@@ -6425,10 +6581,13 @@
// Only navigation bar
if (mNavigationBar != null) {
- if (isNavigationBarOnBottom(displayWidth, displayHeight)) {
+ int position = navigationBarPosition(displayWidth, displayHeight, displayRotation);
+ if (position == NAV_BAR_BOTTOM) {
outInsets.bottom = getNavigationBarHeight(displayRotation, mUiMode);
- } else {
+ } else if (position == NAV_BAR_RIGHT) {
outInsets.right = getNavigationBarWidth(displayRotation, mUiMode);
+ } else if (position == NAV_BAR_LEFT) {
+ outInsets.left = getNavigationBarWidth(displayRotation, mUiMode);
}
}
}
@@ -6774,9 +6933,7 @@
@Override public void run() {
if (mBootMsgDialog == null) {
int theme;
- if (mHasFeatureWatch) {
- theme = com.android.internal.R.style.Theme_Micro_Dialog_Alert;
- } else if (mContext.getPackageManager().hasSystemFeature(FEATURE_TELEVISION)) {
+ if (mContext.getPackageManager().hasSystemFeature(FEATURE_TELEVISION)) {
theme = com.android.internal.R.style.Theme_Leanback_Dialog_Alert;
} else {
theme = 0;
@@ -7165,6 +7322,11 @@
Settings.Global.ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED, 0) == 1;
}
+ private boolean areSystemNavigationKeysEnabled() {
+ return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED, 0, UserHandle.USER_CURRENT) == 1;
+ }
+
@Override
public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always) {
if (!mVibrator.hasVibrator()) {
@@ -7409,7 +7571,9 @@
// we're no longer on the Keyguard and the screen is ready. We can now request the bars.
mPendingPanicGestureUptime = 0;
mStatusBarController.showTransient();
- mNavigationBarController.showTransient();
+ if (!isNavBarEmpty(vis)) {
+ mNavigationBarController.showTransient();
+ }
}
final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested()
@@ -7441,7 +7605,7 @@
if (win != null && oldImmersiveMode != newImmersiveMode) {
final String pkg = win.getOwningPackage();
mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode,
- isUserSetupComplete());
+ isUserSetupComplete(), isNavBarEmpty(win.getSystemUiVisibility()));
}
vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis);
@@ -7500,6 +7664,14 @@
&& canHideNavigationBar();
}
+ private static boolean isNavBarEmpty(int systemUiFlags) {
+ final int disableNavigationBar = (View.STATUS_BAR_DISABLE_HOME
+ | View.STATUS_BAR_DISABLE_BACK
+ | View.STATUS_BAR_DISABLE_RECENT);
+
+ return (systemUiFlags & disableNavigationBar) == disableNavigationBar;
+ }
+
/**
* @return whether the navigation or status bar can be made translucent
*
@@ -7564,6 +7736,43 @@
}
@Override
+ public boolean shouldRotateSeamlessly(int oldRotation, int newRotation) {
+ // For the upside down rotation we don't rotate seamlessly as the navigation
+ // bar moves position.
+ // Note most apps (using orientation:sensor or user as opposed to fullSensor)
+ // will not enter the reverse portrait orientation, so actually the
+ // orientation won't change at all.
+ if (oldRotation == mUpsideDownRotation || newRotation == mUpsideDownRotation) {
+ return false;
+ }
+ int delta = newRotation - oldRotation;
+ if (delta < 0) delta += 4;
+ // Likewise we don't rotate seamlessly for 180 degree rotations
+ // in this case the surfaces never resize, and our logic to
+ // revert the transformations on size change will fail. We could
+ // fix this in the future with the "tagged" frames idea.
+ if (delta == Surface.ROTATION_180) {
+ return false;
+ }
+
+ final WindowState w = mTopFullscreenOpaqueWindowState;
+ if (w != mFocusedWindow) {
+ return false;
+ }
+
+ // We only enable seamless rotation if the top window has requested
+ // it and is in the fullscreen opaque state. Seamless rotation
+ // requires freezing various Surface states and won't work well
+ // with animations, so we disable it in the animation case for now.
+ if (w != null && !w.isAnimatingLw() &&
+ ((w.getAttrs().rotationAnimation == ROTATION_ANIMATION_JUMPCUT) ||
+ (w.getAttrs().rotationAnimation == ROTATION_ANIMATION_SEAMLESS))) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
public void dump(String prefix, PrintWriter pw, String[] args) {
pw.print(prefix); pw.print("mSafeMode="); pw.print(mSafeMode);
pw.print(" mSystemReady="); pw.print(mSystemReady);
diff --git a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
index 80e4341..598c58e 100644
--- a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
@@ -43,6 +43,7 @@
private static final int SWIPE_FROM_TOP = 1;
private static final int SWIPE_FROM_BOTTOM = 2;
private static final int SWIPE_FROM_RIGHT = 3;
+ private static final int SWIPE_FROM_LEFT = 4;
private final Context mContext;
private final int mSwipeStartThreshold;
@@ -127,6 +128,9 @@
} else if (swipe == SWIPE_FROM_RIGHT) {
if (DEBUG) Slog.d(TAG, "Firing onSwipeFromRight");
mCallbacks.onSwipeFromRight();
+ } else if (swipe == SWIPE_FROM_LEFT) {
+ if (DEBUG) Slog.d(TAG, "Firing onSwipeFromLeft");
+ mCallbacks.onSwipeFromLeft();
}
}
break;
@@ -229,6 +233,11 @@
&& elapsed < SWIPE_TIMEOUT_MS) {
return SWIPE_FROM_RIGHT;
}
+ if (fromX <= mSwipeStartThreshold
+ && x > fromX + mSwipeDistanceThreshold
+ && elapsed < SWIPE_TIMEOUT_MS) {
+ return SWIPE_FROM_LEFT;
+ }
return SWIPE_NONE;
}
@@ -265,6 +274,7 @@
void onSwipeFromTop();
void onSwipeFromBottom();
void onSwipeFromRight();
+ void onSwipeFromLeft();
void onFling(int durationMs);
void onDown();
void onUpOrCancel();
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java
index a32c017..8ef0acb 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java
@@ -49,6 +49,7 @@
"debug.orientation.log", false);
private static final boolean USE_GRAVITY_SENSOR = false;
+ private static final int DEFAULT_BATCH_LATENCY = 100000;
private Handler mHandler;
private SensorManager mSensorManager;
@@ -118,7 +119,12 @@
Slog.d(TAG, "WindowOrientationListener enabled");
}
mOrientationJudge.resetLocked();
- mSensorManager.registerListener(mOrientationJudge, mSensor, mRate, mHandler);
+ if (mSensor.getType() == Sensor.TYPE_ACCELEROMETER) {
+ mSensorManager.registerListener(
+ mOrientationJudge, mSensor, mRate, DEFAULT_BATCH_LATENCY, mHandler);
+ } else {
+ mSensorManager.registerListener(mOrientationJudge, mSensor, mRate, mHandler);
+ }
mEnabled = true;
}
}
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 7e27558..4fce49e 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -59,6 +59,7 @@
showingAndNotOccluded = true;
secure = true;
deviceHasKeyguard = true;
+ currentUser = UserHandle.USER_NULL;
}
boolean showing;
boolean showingAndNotOccluded;
@@ -157,6 +158,10 @@
if (mKeyguardState.systemIsReady) {
// If the system is ready, it means keyguard crashed and restarted.
mKeyguardService.onSystemReady();
+ if (mKeyguardState.currentUser != UserHandle.USER_NULL) {
+ // There has been a user switch earlier
+ mKeyguardService.setCurrentUser(mKeyguardState.currentUser);
+ }
// This is used to hide the scrim once keyguard displays.
if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE) {
mKeyguardService.onStartedWakingUp();
@@ -194,6 +199,20 @@
return mKeyguardState.showing;
}
+ public boolean isTrusted() {
+ if (mKeyguardService != null) {
+ return mKeyguardService.isTrusted();
+ }
+ return false;
+ }
+
+ public boolean hasLockscreenWallpaper() {
+ if (mKeyguardService != null) {
+ return mKeyguardService.hasLockscreenWallpaper();
+ }
+ return false;
+ }
+
public boolean isInputRestricted() {
if (mKeyguardService != null) {
mKeyguardState.inputRestricted = mKeyguardService.isInputRestricted();
@@ -215,14 +234,15 @@
public void setOccluded(boolean isOccluded) {
if (mKeyguardService != null) {
+ if (DEBUG) Log.v(TAG, "setOccluded(" + isOccluded + ")");
mKeyguardService.setOccluded(isOccluded);
}
mKeyguardState.occluded = isOccluded;
}
- public void dismiss() {
+ public void dismiss(boolean allowWhileOccluded) {
if (mKeyguardService != null) {
- mKeyguardService.dismiss();
+ mKeyguardService.dismiss(allowWhileOccluded);
}
}
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
index 57e8857..55652fe 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
@@ -81,9 +81,9 @@
}
@Override // Binder interface
- public void dismiss() {
+ public void dismiss(boolean allowWhileOccluded) {
try {
- mService.dismiss();
+ mService.dismiss(allowWhileOccluded);
} catch (RemoteException e) {
Slog.w(TAG , "Remote Exception", e);
}
@@ -234,6 +234,14 @@
return mKeyguardStateMonitor.isShowing();
}
+ public boolean isTrusted() {
+ return mKeyguardStateMonitor.isTrusted();
+ }
+
+ public boolean hasLockscreenWallpaper() {
+ return mKeyguardStateMonitor.hasLockscreenWallpaper();
+ }
+
public boolean isSecure(int userId) {
return mKeyguardStateMonitor.isSecure(userId);
}
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
index 138f068..08eaaa9 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
@@ -43,6 +43,8 @@
private volatile boolean mIsShowing = true;
private volatile boolean mSimSecure = true;
private volatile boolean mInputRestricted = true;
+ private volatile boolean mTrusted = false;
+ private volatile boolean mHasLockscreenWallpaper = false;
private int mCurrentUserId;
@@ -70,6 +72,14 @@
return mInputRestricted;
}
+ public boolean isTrusted() {
+ return mTrusted;
+ }
+
+ public boolean hasLockscreenWallpaper() {
+ return mHasLockscreenWallpaper;
+ }
+
@Override // Binder interface
public void onShowingStateChanged(boolean showing) {
mIsShowing = showing;
@@ -93,12 +103,23 @@
mInputRestricted = inputRestricted;
}
+ @Override // Binder interface
+ public void onTrustedChanged(boolean trusted) {
+ mTrusted = trusted;
+ }
+
+ @Override // Binder interface
+ public void onHasLockscreenWallpaperChanged(boolean hasLockscreenWallpaper) {
+ mHasLockscreenWallpaper = hasLockscreenWallpaper;
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.println(prefix + TAG);
prefix += " ";
pw.println(prefix + "mIsShowing=" + mIsShowing);
pw.println(prefix + "mSimSecure=" + mSimSecure);
pw.println(prefix + "mInputRestricted=" + mInputRestricted);
+ pw.println(prefix + "mTrusted=" + mTrusted);
pw.println(prefix + "mCurrentUserId=" + mCurrentUserId);
}
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 7108f4a..4e9f5a2 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -18,6 +18,7 @@
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
+import android.app.RetailDemoModeServiceInternal;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
@@ -91,6 +92,7 @@
private final ActivityManagerInternal mActivityManagerInternal;
private final InputManagerInternal mInputManagerInternal;
private final InputMethodManagerInternal mInputMethodManagerInternal;
+ private final RetailDemoModeServiceInternal mRetailDemoModeServiceInternal;
private final NotifierHandler mHandler;
private final Intent mScreenOnIntent;
@@ -136,6 +138,7 @@
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
+ mRetailDemoModeServiceInternal = LocalServices.getService(RetailDemoModeServiceInternal.class);
mHandler = new NotifierHandler(looper);
mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
@@ -191,6 +194,48 @@
}
}
+ public void onLongPartialWakeLockStart(String tag, int ownerUid, WorkSource workSource,
+ String historyTag) {
+ if (DEBUG) {
+ Slog.d(TAG, "onLongPartialWakeLockStart: ownerUid=" + ownerUid
+ + ", workSource=" + workSource);
+ }
+
+ try {
+ if (workSource != null) {
+ final int N = workSource.size();
+ for (int i=0; i<N; i++) {
+ mBatteryStats.noteLongPartialWakelockStart(tag, historyTag, workSource.get(i));
+ }
+ } else {
+ mBatteryStats.noteLongPartialWakelockStart(tag, historyTag, ownerUid);
+ }
+ } catch (RemoteException ex) {
+ // Ignore
+ }
+ }
+
+ public void onLongPartialWakeLockFinish(String tag, int ownerUid, WorkSource workSource,
+ String historyTag) {
+ if (DEBUG) {
+ Slog.d(TAG, "onLongPartialWakeLockFinish: ownerUid=" + ownerUid
+ + ", workSource=" + workSource);
+ }
+
+ try {
+ if (workSource != null) {
+ final int N = workSource.size();
+ for (int i=0; i<N; i++) {
+ mBatteryStats.noteLongPartialWakelockFinish(tag, historyTag, workSource.get(i));
+ }
+ } else {
+ mBatteryStats.noteLongPartialWakelockFinish(tag, historyTag, ownerUid);
+ }
+ } catch (RemoteException ex) {
+ // Ignore
+ }
+ }
+
/**
* Called when a wake lock is changing.
*/
@@ -534,7 +579,9 @@
}
mUserActivityPending = false;
}
-
+ if (mRetailDemoModeServiceInternal != null) {
+ mRetailDemoModeServiceInternal.onUserActivity();
+ }
mPolicy.userActivity();
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 12a2d2e..2824e6e 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -56,6 +56,7 @@
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.util.EventLog;
+import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseIntArray;
import android.util.TimeUtils;
@@ -73,9 +74,7 @@
import com.android.server.am.BatteryStatsService;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
-import com.android.server.vr.VrManagerInternal;
import com.android.server.vr.VrManagerService;
-import com.android.server.vr.VrStateListener;
import libcore.util.Objects;
import java.io.FileDescriptor;
@@ -108,6 +107,8 @@
private static final int MSG_SANDMAN = 2;
// Message: Sent when the screen brightness boost expires.
private static final int MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT = 3;
+ // Message: Polling to look for long held wake locks.
+ private static final int MSG_CHECK_FOR_LONG_WAKELOCKS = 4;
// Dirty bit: mWakeLocks changed
private static final int DIRTY_WAKE_LOCKS = 1 << 0;
@@ -159,6 +160,9 @@
// This should perhaps be a setting.
private static final int SCREEN_BRIGHTNESS_BOOST_TIMEOUT = 5 * 1000;
+ // How long a partial wake lock must be held until we consider it a long wake lock.
+ static final long MIN_LONG_WAKE_CHECK_INTERVAL = 60*1000;
+
// Power hints defined in hardware/libhardware/include/hardware/power.h.
private static final int POWER_HINT_LOW_POWER = 5;
private static final int POWER_HINT_VR_MODE = 7;
@@ -221,6 +225,15 @@
// A bitfield that summarizes the state of all active wakelocks.
private int mWakeLockSummary;
+ // Have we scheduled a message to check for long wake locks? This is when we will check.
+ private long mNotifyLongScheduled;
+
+ // Last time we checked for long wake locks.
+ private long mNotifyLongDispatched;
+
+ // The time we decided to do next long check.
+ private long mNotifyLongNextCheck;
+
// If true, instructs the display controller to wait for the proximity sensor to
// go negative before turning the screen on.
private boolean mRequestWaitForNegativeProximity;
@@ -1025,6 +1038,38 @@
mNotifier.onWakeLockAcquired(wakeLock.mFlags, wakeLock.mTag, wakeLock.mPackageName,
wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource,
wakeLock.mHistoryTag);
+ restartNofifyLongTimerLocked(wakeLock);
+ }
+ }
+
+ private void enqueueNotifyLongMsgLocked(long time) {
+ mNotifyLongScheduled = time;
+ Message msg = mHandler.obtainMessage(MSG_CHECK_FOR_LONG_WAKELOCKS);
+ msg.setAsynchronous(true);
+ mHandler.sendMessageAtTime(msg, time);
+ }
+
+ private void restartNofifyLongTimerLocked(WakeLock wakeLock) {
+ wakeLock.mAcquireTime = SystemClock.uptimeMillis();
+ if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK)
+ == PowerManager.PARTIAL_WAKE_LOCK && mNotifyLongScheduled == 0) {
+ enqueueNotifyLongMsgLocked(wakeLock.mAcquireTime + MIN_LONG_WAKE_CHECK_INTERVAL);
+ }
+ }
+
+ private void notifyWakeLockLongStartedLocked(WakeLock wakeLock) {
+ if (mSystemReady && !wakeLock.mDisabled) {
+ wakeLock.mNotifiedLong = true;
+ mNotifier.onLongPartialWakeLockStart(wakeLock.mTag, wakeLock.mOwnerUid,
+ wakeLock.mWorkSource, wakeLock.mHistoryTag);
+ }
+ }
+
+ private void notifyWakeLockLongFinishedLocked(WakeLock wakeLock) {
+ if (wakeLock.mNotifiedLong) {
+ wakeLock.mNotifiedLong = false;
+ mNotifier.onLongPartialWakeLockFinish(wakeLock.mTag, wakeLock.mOwnerUid,
+ wakeLock.mWorkSource, wakeLock.mHistoryTag);
}
}
@@ -1034,15 +1079,23 @@
mNotifier.onWakeLockChanging(wakeLock.mFlags, wakeLock.mTag, wakeLock.mPackageName,
wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource,
wakeLock.mHistoryTag, flags, tag, packageName, uid, pid, ws, historyTag);
+ notifyWakeLockLongFinishedLocked(wakeLock);
+ // Changing the wake lock will count as releasing the old wake lock(s) and
+ // acquiring the new ones... we do this because otherwise once a wakelock
+ // becomes long, if we just continued to treat it as long we can get in to
+ // situations where we spam battery stats with every following change to it.
+ restartNofifyLongTimerLocked(wakeLock);
}
}
private void notifyWakeLockReleasedLocked(WakeLock wakeLock) {
if (mSystemReady && wakeLock.mNotifiedAcquired) {
wakeLock.mNotifiedAcquired = false;
+ wakeLock.mAcquireTime = 0;
mNotifier.onWakeLockReleased(wakeLock.mFlags, wakeLock.mTag,
wakeLock.mPackageName, wakeLock.mOwnerUid, wakeLock.mOwnerPid,
wakeLock.mWorkSource, wakeLock.mHistoryTag);
+ notifyWakeLockLongFinishedLocked(wakeLock);
}
}
@@ -1599,6 +1652,42 @@
}
}
+ void checkForLongWakeLocks() {
+ synchronized (mLock) {
+ final long now = SystemClock.uptimeMillis();
+ mNotifyLongDispatched = now;
+ final long when = now - MIN_LONG_WAKE_CHECK_INTERVAL;
+ long nextCheckTime = Long.MAX_VALUE;
+ final int numWakeLocks = mWakeLocks.size();
+ for (int i = 0; i < numWakeLocks; i++) {
+ final WakeLock wakeLock = mWakeLocks.get(i);
+ if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK)
+ == PowerManager.PARTIAL_WAKE_LOCK) {
+ if (wakeLock.mNotifiedAcquired && !wakeLock.mNotifiedLong) {
+ if (wakeLock.mAcquireTime < when) {
+ // This wake lock has exceeded the long acquire time, report!
+ notifyWakeLockLongStartedLocked(wakeLock);
+ } else {
+ // This wake lock could still become a long one, at this time.
+ long checkTime = wakeLock.mAcquireTime + MIN_LONG_WAKE_CHECK_INTERVAL;
+ if (checkTime < nextCheckTime) {
+ nextCheckTime = checkTime;
+ }
+ }
+ }
+ }
+ }
+ mNotifyLongScheduled = 0;
+ mHandler.removeMessages(MSG_CHECK_FOR_LONG_WAKELOCKS);
+ if (nextCheckTime != Long.MAX_VALUE) {
+ mNotifyLongNextCheck = nextCheckTime;
+ enqueueNotifyLongMsgLocked(nextCheckTime);
+ } else {
+ mNotifyLongNextCheck = 0;
+ }
+ }
+ }
+
/**
* Updates the value of mUserActivitySummary to summarize the user requested
* state of the system such as whether the screen should be bright or dim.
@@ -1996,7 +2085,12 @@
float screenAutoBrightnessAdjustment = 0.0f;
boolean autoBrightness = (mScreenBrightnessModeSetting ==
Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
- if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) {
+ if (!mBootCompleted) {
+ // Keep the brightness steady during boot. This requires the
+ // bootloader brightness and the default brightness to be identical.
+ autoBrightness = false;
+ brightnessSetByUser = false;
+ } else if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) {
screenBrightness = mScreenBrightnessOverrideFromWindowManager;
autoBrightness = false;
brightnessSetByUser = false;
@@ -2748,6 +2842,27 @@
pw.println(" mHalAutoSuspendModeEnabled=" + mHalAutoSuspendModeEnabled);
pw.println(" mHalInteractiveModeEnabled=" + mHalInteractiveModeEnabled);
pw.println(" mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary));
+ pw.print(" mNotifyLongScheduled=");
+ if (mNotifyLongScheduled == 0) {
+ pw.print("(none)");
+ } else {
+ TimeUtils.formatDuration(mNotifyLongScheduled, SystemClock.uptimeMillis(), pw);
+ }
+ pw.println();
+ pw.print(" mNotifyLongDispatched=");
+ if (mNotifyLongDispatched == 0) {
+ pw.print("(none)");
+ } else {
+ TimeUtils.formatDuration(mNotifyLongDispatched, SystemClock.uptimeMillis(), pw);
+ }
+ pw.println();
+ pw.print(" mNotifyLongNextCheck=");
+ if (mNotifyLongNextCheck == 0) {
+ pw.print("(none)");
+ } else {
+ TimeUtils.formatDuration(mNotifyLongNextCheck, SystemClock.uptimeMillis(), pw);
+ }
+ pw.println();
pw.println(" mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary));
pw.println(" mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity);
pw.println(" mSandmanScheduled=" + mSandmanScheduled);
@@ -2856,6 +2971,10 @@
}
pw.println();
+ pw.println("Looper state:");
+ mHandler.getLooper().dump(new PrintWriterPrinter(pw), " ");
+
+ pw.println();
pw.println("Wake Locks: size=" + mWakeLocks.size());
for (WakeLock wl : mWakeLocks) {
pw.println(" " + wl);
@@ -2984,6 +3103,9 @@
case MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT:
handleScreenBrightnessBoostTimeout();
break;
+ case MSG_CHECK_FOR_LONG_WAKELOCKS:
+ checkForLongWakeLocks();
+ break;
}
}
}
@@ -3000,7 +3122,9 @@
public String mHistoryTag;
public final int mOwnerUid;
public final int mOwnerPid;
+ public long mAcquireTime;
public boolean mNotifiedAcquired;
+ public boolean mNotifiedLong;
public boolean mDisabled;
public WakeLock(IBinder lock, int flags, String tag, String packageName,
@@ -3059,9 +3183,34 @@
@Override
public String toString() {
- return getLockLevelString()
- + " '" + mTag + "'" + getLockFlagsString() + (mDisabled ? " DISABLED" : "")
- + " (uid=" + mOwnerUid + ", pid=" + mOwnerPid + ", ws=" + mWorkSource + ")";
+ StringBuilder sb = new StringBuilder();
+ sb.append(getLockLevelString());
+ sb.append(" '");
+ sb.append(mTag);
+ sb.append("'");
+ sb.append(getLockFlagsString());
+ if (mDisabled) {
+ sb.append(" DISABLED");
+ }
+ if (mNotifiedAcquired) {
+ sb.append(" ACQ=");
+ TimeUtils.formatDuration(mAcquireTime-SystemClock.uptimeMillis(), sb);
+ }
+ if (mNotifiedLong) {
+ sb.append(" LONG");
+ }
+ sb.append(" (uid=");
+ sb.append(mOwnerUid);
+ if (mOwnerPid != 0) {
+ sb.append(" pid=");
+ sb.append(mOwnerPid);
+ }
+ if (mWorkSource != null) {
+ sb.append(" ws=");
+ sb.append(mWorkSource);
+ }
+ sb.append(")");
+ return sb.toString();
}
@SuppressWarnings("deprecation")
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 27d1671..3d2839f 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -543,7 +543,8 @@
}
try {
- bluetoothOff = bluetooth == null || !bluetooth.isEnabled();
+ bluetoothOff = bluetooth == null ||
+ bluetooth.getState() == BluetoothAdapter.STATE_OFF;
if (!bluetoothOff) {
Log.w(TAG, "Disabling Bluetooth...");
bluetooth.disable(false); // disable but don't persist new state
@@ -577,7 +578,7 @@
if (!bluetoothOff) {
try {
- bluetoothOff = !bluetooth.isEnabled();
+ bluetoothOff = bluetooth.getState() == BluetoothAdapter.STATE_OFF;
} catch (RemoteException ex) {
Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
bluetoothOff = true;
diff --git a/services/core/java/com/android/server/search/SearchManagerService.java b/services/core/java/com/android/server/search/SearchManagerService.java
index 4d91814..f3b9b18 100644
--- a/services/core/java/com/android/server/search/SearchManagerService.java
+++ b/services/core/java/com/android/server/search/SearchManagerService.java
@@ -33,6 +33,7 @@
import android.database.ContentObserver;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -42,6 +43,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -57,6 +59,7 @@
*/
public class SearchManagerService extends ISearchManager.Stub {
private static final String TAG = "SearchManagerService";
+ final Handler mHandler;
public static class Lifecycle extends SystemService {
private SearchManagerService mService;
@@ -72,8 +75,13 @@
}
@Override
- public void onUnlockUser(int userHandle) {
- mService.onUnlockUser(userHandle);
+ public void onUnlockUser(final int userId) {
+ mService.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mService.onUnlockUser(userId);
+ }
+ });
}
@Override
@@ -99,6 +107,7 @@
mContext = context;
new MyPackageMonitor().register(context, null, UserHandle.ALL, true);
new GlobalSearchProviderObserver(context.getContentResolver());
+ mHandler = BackgroundThread.getHandler();
}
private Searchables getSearchables(int userId) {
@@ -132,7 +141,12 @@
}
private void onUnlockUser(int userId) {
- getSearchables(userId, true);
+ try {
+ getSearchables(userId, true);
+ } catch (IllegalStateException ignored) {
+ // We're just trying to warm a cache, so we don't mind if the user
+ // was stopped or destroyed before we got here.
+ }
}
private void onCleanupUser(int userId) {
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index baa7f1e..552803f 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -29,8 +29,11 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
+import android.view.KeyEvent;
+
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
@@ -415,6 +418,18 @@
}
@Override
+ public void handleSystemNavigationKey(int key) throws RemoteException {
+ enforceExpandStatusBar();
+
+ if (mBar != null) {
+ try {
+ mBar.handleSystemNavigationKey(key);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ @Override
public void disable(int what, IBinder token, String pkg) {
disableForUser(what, token, pkg, mCurrentUserId);
}
@@ -928,6 +943,20 @@
+ " token=" + tok.token);
}
pw.println(" mCurrentUserId=" + mCurrentUserId);
+ pw.println(" mIcons=");
+ for (String slot : mIcons.keySet()) {
+ pw.println(" ");
+ pw.print(slot);
+ pw.print(" -> ");
+ final StatusBarIcon icon = mIcons.get(slot);
+ pw.print(icon);
+ if (!TextUtils.isEmpty(icon.contentDescription)) {
+ pw.print(" \"");
+ pw.print(icon.contentDescription);
+ pw.print("\"");
+ }
+ pw.println();
+ }
}
}
}
diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
index cbbcb0e..0ae1717 100644
--- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
+++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
@@ -455,9 +455,7 @@
//log the event to event log with the amount of free storage(in bytes) left on the device
EventLog.writeEvent(EventLogTags.LOW_STORAGE, mFreeMem);
// Pack up the values and broadcast them to everyone
- Intent lowMemIntent = new Intent(Environment.isExternalStorageEmulated()
- ? Settings.ACTION_INTERNAL_STORAGE_SETTINGS
- : Intent.ACTION_MANAGE_PACKAGE_STORAGE);
+ Intent lowMemIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
lowMemIntent.putExtra("memory", mFreeMem);
lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
NotificationManager mNotificationMgr =
diff --git a/services/core/java/com/android/server/trust/TrustArchive.java b/services/core/java/com/android/server/trust/TrustArchive.java
index fd63d48..aaac297 100644
--- a/services/core/java/com/android/server/trust/TrustArchive.java
+++ b/services/core/java/com/android/server/trust/TrustArchive.java
@@ -37,6 +37,7 @@
private static final int TYPE_AGENT_CONNECTED = 4;
private static final int TYPE_AGENT_STOPPED = 5;
private static final int TYPE_MANAGING_TRUST = 6;
+ private static final int TYPE_POLICY_CHANGED = 7;
private static final int HISTORY_LIMIT = 200;
@@ -99,6 +100,10 @@
addEvent(new Event(TYPE_MANAGING_TRUST, userId, agent, null, 0, 0, managing));
}
+ public void logDevicePolicyChanged() {
+ addEvent(new Event(TYPE_POLICY_CHANGED, UserHandle.USER_ALL, null, null, 0, 0, false));
+ }
+
private void addEvent(Event e) {
if (mEvents.size() >= HISTORY_LIMIT) {
mEvents.removeFirst();
@@ -112,7 +117,8 @@
Iterator<Event> iter = mEvents.descendingIterator();
while (iter.hasNext() && count < limit) {
Event ev = iter.next();
- if (userId != UserHandle.USER_ALL && userId != ev.userId) {
+ if (userId != UserHandle.USER_ALL && userId != ev.userId
+ && ev.userId != UserHandle.USER_ALL) {
continue;
}
@@ -122,11 +128,13 @@
if (userId == UserHandle.USER_ALL) {
writer.print("user="); writer.print(ev.userId); writer.print(", ");
}
- writer.print("agent=");
- if (duplicateSimpleNames) {
- writer.print(ev.agent.flattenToShortString());
- } else {
- writer.print(getSimpleName(ev.agent));
+ if (ev.agent != null) {
+ writer.print("agent=");
+ if (duplicateSimpleNames) {
+ writer.print(ev.agent.flattenToShortString());
+ } else {
+ writer.print(getSimpleName(ev.agent));
+ }
}
switch (ev.type) {
case TYPE_GRANT_TRUST:
@@ -181,6 +189,8 @@
return "AgentStopped";
case TYPE_MANAGING_TRUST:
return "ManagingTrust";
+ case TYPE_POLICY_CHANGED:
+ return "DevicePolicyChanged";
default:
return "Unknown(" + type + ")";
}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index c1868a4..d9c4254 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -399,12 +399,17 @@
}
void updateDevicePolicyFeatures() {
+ boolean changed = false;
for (int i = 0; i < mActiveAgents.size(); i++) {
AgentInfo info = mActiveAgents.valueAt(i);
if (info.agent.isConnected()) {
info.agent.updateDevicePolicyFeatures();
+ changed = true;
}
}
+ if (changed) {
+ mArchive.logDevicePolicyChanged();
+ }
}
private void removeAgentsOfPackage(String packageName) {
diff --git a/services/core/java/com/android/server/twilight/TwilightListener.java b/services/core/java/com/android/server/twilight/TwilightListener.java
index 29ead44..58dcef6 100644
--- a/services/core/java/com/android/server/twilight/TwilightListener.java
+++ b/services/core/java/com/android/server/twilight/TwilightListener.java
@@ -16,6 +16,14 @@
package com.android.server.twilight;
+import android.annotation.Nullable;
+
+/**
+ * Callback for when the twilight state has changed.
+ */
public interface TwilightListener {
- void onTwilightStateChanged();
+ /**
+ * Called when the twilight state has changed.
+ */
+ void onTwilightStateChanged(@Nullable TwilightState state);
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/twilight/TwilightManager.java b/services/core/java/com/android/server/twilight/TwilightManager.java
index 56137a4..5ef9417 100644
--- a/services/core/java/com/android/server/twilight/TwilightManager.java
+++ b/services/core/java/com/android/server/twilight/TwilightManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * 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.
@@ -16,10 +16,30 @@
package com.android.server.twilight;
+import android.annotation.NonNull;
import android.os.Handler;
+/**
+ * This class provides sunrise/sunset information based on the device's current location.
+ */
public interface TwilightManager {
- void registerListener(TwilightListener listener, Handler handler);
- void unregisterListener(TwilightListener listener);
- TwilightState getCurrentState();
+ /**
+ * Register a listener to be notified whenever the twilight state changes.
+ *
+ * @param listener the {@link TwilightListener} to be notified
+ * @param handler the {@link Handler} to use to notify the listener
+ */
+ void registerListener(@NonNull TwilightListener listener, @NonNull Handler handler);
+
+ /**
+ * Unregisters a previously registered listener.
+ *
+ * @param listener the {@link TwilightListener} to be unregistered
+ */
+ void unregisterListener(@NonNull TwilightListener listener);
+
+ /**
+ * Returns the last {@link TwilightState}, or {@code null} if not available.
+ */
+ TwilightState getLastTwilightState();
}
diff --git a/services/core/java/com/android/server/twilight/TwilightService.java b/services/core/java/com/android/server/twilight/TwilightService.java
index 6158c92..acd6587 100644
--- a/services/core/java/com/android/server/twilight/TwilightService.java
+++ b/services/core/java/com/android/server/twilight/TwilightService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -16,618 +16,290 @@
package com.android.server.twilight;
-import com.android.server.SystemService;
-import com.android.server.TwilightCalculator;
-
-import android.app.ActivityManager;
+import android.annotation.NonNull;
import android.app.AlarmManager;
-import android.app.PendingIntent;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.database.ContentObserver;
-import android.location.Criteria;
+import android.icu.impl.CalendarAstronomer;
+import android.icu.util.Calendar;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.provider.Settings.Secure;
-import android.text.format.DateUtils;
-import android.text.format.Time;
+import android.util.ArrayMap;
import android.util.Slog;
-import java.util.ArrayList;
-import java.util.Iterator;
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.SystemService;
-import libcore.util.Objects;
+import java.util.Objects;
/**
* Figures out whether it's twilight time based on the user's location.
- *
+ * <p>
* Used by the UI mode manager and other components to adjust night mode
* effects based on sunrise and sunset.
*/
-public final class TwilightService extends SystemService {
- static final String TAG = "TwilightService";
- static final boolean DEBUG = false;
- static final String ACTION_UPDATE_TWILIGHT_STATE =
- "com.android.server.action.UPDATE_TWILIGHT_STATE";
+public final class TwilightService extends SystemService
+ implements AlarmManager.OnAlarmListener, Handler.Callback, LocationListener {
- // The amount of time after or before sunrise over which to start adjusting
- // twilight affected things. We want the change to happen gradually so that
- // it is below the threshold of perceptibility and so that the adjustment has
- // maximum effect well after dusk.
- private static final long TWILIGHT_ADJUSTMENT_TIME = DateUtils.HOUR_IN_MILLIS * 2;
+ private static final String TAG = "TwilightService";
+ private static final boolean DEBUG = false;
- // Broadcast when twilight changes.
- public static final String ACTION_TWILIGHT_CHANGED = "android.intent.action.TWILIGHT_CHANGED";
+ private static final int MSG_START_LISTENING = 1;
+ private static final int MSG_STOP_LISTENING = 2;
- public static final String EXTRA_IS_NIGHT = "isNight";
- public static final String EXTRA_AMOUNT = "amount";
+ @GuardedBy("mListeners")
+ private final ArrayMap<TwilightListener, Handler> mListeners = new ArrayMap<>();
- // Amount of time the TwilightService will stay locked in an override state before switching
- // back to auto.
- private static final long RESET_TIME = DateUtils.HOUR_IN_MILLIS * 2;
- private static final String EXTRA_RESET_USER = "user";
+ private final Handler mHandler;
- private static final String ACTION_RESET_TWILIGHT_AUTO =
- "com.android.server.action.RESET_TWILIGHT_AUTO";
+ private AlarmManager mAlarmManager;
+ private LocationManager mLocationManager;
- final Object mLock = new Object();
-
- AlarmManager mAlarmManager;
- LocationManager mLocationManager;
- LocationHandler mLocationHandler;
-
- final ArrayList<TwilightListenerRecord> mListeners =
- new ArrayList<TwilightListenerRecord>();
-
- TwilightState mTwilightState;
-
- private int mCurrentUser;
- private boolean mLocked;
private boolean mBootCompleted;
+ private boolean mHasListeners;
+
+ private BroadcastReceiver mTimeChangedReceiver;
+ private Location mLastLocation;
+
+ @GuardedBy("mListeners")
+ private TwilightState mLastTwilightState;
public TwilightService(Context context) {
super(context);
+ mHandler = new Handler(Looper.getMainLooper(), this);
}
@Override
public void onStart() {
- mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
- mLocationManager = (LocationManager) getContext().getSystemService(
- Context.LOCATION_SERVICE);
- mLocationHandler = new LocationHandler();
- mCurrentUser = ActivityManager.getCurrentUser();
+ publishLocalService(TwilightManager.class, new TwilightManager() {
+ @Override
+ public void registerListener(@NonNull TwilightListener listener,
+ @NonNull Handler handler) {
+ synchronized (mListeners) {
+ final boolean wasEmpty = mListeners.isEmpty();
+ mListeners.put(listener, handler);
- IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- filter.addAction(Intent.ACTION_TIME_CHANGED);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
- filter.addAction(Intent.ACTION_USER_SWITCHED);
- filter.addAction(ACTION_UPDATE_TWILIGHT_STATE);
- getContext().registerReceiver(mReceiver, filter);
+ if (wasEmpty && !mListeners.isEmpty()) {
+ mHandler.sendEmptyMessage(MSG_START_LISTENING);
+ }
+ }
+ }
- publishLocalService(TwilightManager.class, mService);
+ @Override
+ public void unregisterListener(@NonNull TwilightListener listener) {
+ synchronized (mListeners) {
+ final boolean wasEmpty = mListeners.isEmpty();
+ mListeners.remove(listener);
+
+ if (!wasEmpty && mListeners.isEmpty()) {
+ mHandler.sendEmptyMessage(MSG_STOP_LISTENING);
+ }
+ }
+ }
+
+ @Override
+ public TwilightState getLastTwilightState() {
+ synchronized (mListeners) {
+ return mLastTwilightState;
+ }
+ }
+ });
}
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_BOOT_COMPLETED) {
- getContext().getContentResolver().registerContentObserver(
- Secure.getUriFor(Secure.TWILIGHT_MODE), false, mContentObserver, mCurrentUser);
- mContentObserver.onChange(true);
+ final Context c = getContext();
+ mAlarmManager = (AlarmManager) c.getSystemService(Context.ALARM_SERVICE);
+ mLocationManager = (LocationManager) c.getSystemService(Context.LOCATION_SERVICE);
+
mBootCompleted = true;
- sendBroadcast();
- }
- }
-
- private void reregisterSettingObserver() {
- final ContentResolver contentResolver = getContext().getContentResolver();
- contentResolver.unregisterContentObserver(mContentObserver);
- contentResolver.registerContentObserver(Secure.getUriFor(Secure.TWILIGHT_MODE), false,
- mContentObserver, mCurrentUser);
- mContentObserver.onChange(true);
- }
-
- private void setLockedState(TwilightState state) {
- synchronized (mLock) {
- // Make sure we aren't locked so we can set the state.
- mLocked = false;
- setTwilightState(state);
- // Make sure we leave the state locked, so it cant be changed.
- mLocked = true;
- // TODO: Don't bother updating state when locked.
- }
- }
-
- private void setTwilightState(TwilightState state) {
- synchronized (mLock) {
- if (mLocked) {
- // State has been locked by secure setting, shouldn't be changed.
- return;
- }
- if (!Objects.equal(mTwilightState, state)) {
- if (DEBUG) {
- Slog.d(TAG, "Twilight state changed: " + state);
- }
-
- mTwilightState = state;
-
- final int listenerLen = mListeners.size();
- for (int i = 0; i < listenerLen; i++) {
- mListeners.get(i).postUpdate();
- }
- }
- }
- sendBroadcast();
- }
-
- private void sendBroadcast() {
- synchronized (mLock) {
- if (mTwilightState == null) {
- return;
- }
- if (mBootCompleted) {
- Intent intent = new Intent(ACTION_TWILIGHT_CHANGED);
- intent.putExtra(EXTRA_IS_NIGHT, mTwilightState.isNight());
- intent.putExtra(EXTRA_AMOUNT, mTwilightState.getAmount());
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ if (mHasListeners) {
+ startListening();
}
}
}
- private void scheduleReset() {
- long resetTime = System.currentTimeMillis() + RESET_TIME;
- Intent resetIntent = new Intent(ACTION_RESET_TWILIGHT_AUTO);
- resetIntent.putExtra(EXTRA_RESET_USER, mCurrentUser);
- PendingIntent pendingIntent = PendingIntent.getBroadcast(
- getContext(), 0, resetIntent, 0);
- mAlarmManager.cancel(pendingIntent);
- mAlarmManager.setExact(AlarmManager.RTC, resetTime, pendingIntent);
- }
-
- private static class TwilightListenerRecord implements Runnable {
- private final TwilightListener mListener;
- private final Handler mHandler;
-
- public TwilightListenerRecord(TwilightListener listener, Handler handler) {
- mListener = listener;
- mHandler = handler;
- }
-
- public void postUpdate() {
- mHandler.post(this);
- }
-
- @Override
- public void run() {
- mListener.onTwilightStateChanged();
- }
-
- }
-
- private final TwilightManager mService = new TwilightManager() {
- /**
- * Gets the current twilight state.
- *
- * @return The current twilight state, or null if no information is available.
- */
- @Override
- public TwilightState getCurrentState() {
- synchronized (mLock) {
- return mTwilightState;
- }
- }
-
- /**
- * Listens for twilight time.
- *
- * @param listener The listener.
- */
- @Override
- public void registerListener(TwilightListener listener, Handler handler) {
- synchronized (mLock) {
- mListeners.add(new TwilightListenerRecord(listener, handler));
-
- if (mListeners.size() == 1) {
- mLocationHandler.enableLocationUpdates();
- }
- }
- }
-
- @Override
- public void unregisterListener(TwilightListener listener) {
- synchronized (mLock) {
- for (int i = 0; i < mListeners.size(); i++) {
- if (mListeners.get(i).mListener == listener) {
- mListeners.remove(i);
+ @Override
+ public boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_START_LISTENING:
+ if (!mHasListeners) {
+ mHasListeners = true;
+ if (mBootCompleted) {
+ startListening();
}
}
-
- if (mListeners.size() == 0) {
- mLocationHandler.disableLocationUpdates();
+ return true;
+ case MSG_STOP_LISTENING:
+ if (mHasListeners) {
+ mHasListeners = false;
+ if (mBootCompleted) {
+ stopListening();
+ }
}
- }
+ return true;
}
- };
-
- // The user has moved if the accuracy circles of the two locations don't overlap.
- private static boolean hasMoved(Location from, Location to) {
- if (to == null) {
- return false;
- }
-
- if (from == null) {
- return true;
- }
-
- // if new location is older than the current one, the device hasn't moved.
- if (to.getElapsedRealtimeNanos() < from.getElapsedRealtimeNanos()) {
- return false;
- }
-
- // Get the distance between the two points.
- float distance = from.distanceTo(to);
-
- // Get the total accuracy radius for both locations.
- float totalAccuracy = from.getAccuracy() + to.getAccuracy();
-
- // If the distance is greater than the combined accuracy of the two
- // points then they can't overlap and hence the user has moved.
- return distance >= totalAccuracy;
+ return false;
}
- private final class LocationHandler extends Handler {
- private static final int MSG_ENABLE_LOCATION_UPDATES = 1;
- private static final int MSG_GET_NEW_LOCATION_UPDATE = 2;
- private static final int MSG_PROCESS_NEW_LOCATION = 3;
- private static final int MSG_DO_TWILIGHT_UPDATE = 4;
- private static final int MSG_DISABLE_LOCATION_UPDATES = 5;
+ private void startListening() {
+ if (DEBUG) Slog.d(TAG, "startListening");
- private static final long LOCATION_UPDATE_MS = 24 * DateUtils.HOUR_IN_MILLIS;
- private static final long MIN_LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
- private static final float LOCATION_UPDATE_DISTANCE_METER = 1000 * 20;
- private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MIN = 5000;
- private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX =
- 15 * DateUtils.MINUTE_IN_MILLIS;
- private static final double FACTOR_GMT_OFFSET_LONGITUDE =
- 1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS;
+ // Start listening for location updates (default: low power, max 1h, min 10m).
+ mLocationManager.requestLocationUpdates(
+ null /* default */, this, Looper.getMainLooper());
- private boolean mPassiveListenerEnabled;
- private boolean mNetworkListenerEnabled;
- private boolean mDidFirstInit;
- private long mLastNetworkRegisterTime = -MIN_LOCATION_UPDATE_MS;
- private long mLastUpdateInterval;
- private Location mLocation;
- private final TwilightCalculator mTwilightCalculator = new TwilightCalculator();
-
- public void processNewLocation(Location location) {
- Message msg = obtainMessage(MSG_PROCESS_NEW_LOCATION, location);
- sendMessage(msg);
+ // Request the device's location immediately if a previous location isn't available.
+ if (mLocationManager.getLastLocation() == null) {
+ if (mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
+ mLocationManager.requestSingleUpdate(
+ LocationManager.NETWORK_PROVIDER, this, Looper.getMainLooper());
+ } else if (mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
+ mLocationManager.requestSingleUpdate(
+ LocationManager.GPS_PROVIDER, this, Looper.getMainLooper());
+ }
}
- public void enableLocationUpdates() {
- sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES);
- }
-
- public void disableLocationUpdates() {
- sendEmptyMessage(MSG_DISABLE_LOCATION_UPDATES);
- }
-
- public void requestLocationUpdate() {
- sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE);
- }
-
- public void requestTwilightUpdate() {
- sendEmptyMessage(MSG_DO_TWILIGHT_UPDATE);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_PROCESS_NEW_LOCATION: {
- final Location location = (Location)msg.obj;
- final boolean hasMoved = hasMoved(mLocation, location);
- final boolean hasBetterAccuracy = mLocation == null
- || location.getAccuracy() < mLocation.getAccuracy();
- if (DEBUG) {
- Slog.d(TAG, "Processing new location: " + location
- + ", hasMoved=" + hasMoved
- + ", hasBetterAccuracy=" + hasBetterAccuracy);
- }
- if (hasMoved || hasBetterAccuracy) {
- setLocation(location);
- }
- break;
- }
-
- case MSG_GET_NEW_LOCATION_UPDATE:
- if (!mNetworkListenerEnabled) {
- // Don't do anything -- we are still trying to get a
- // location.
- return;
- }
- if ((mLastNetworkRegisterTime + MIN_LOCATION_UPDATE_MS) >=
- SystemClock.elapsedRealtime()) {
- // Don't do anything -- it hasn't been long enough
- // since we last requested an update.
- return;
- }
-
- // Unregister the current location monitor, so we can
- // register a new one for it to get an immediate update.
- mNetworkListenerEnabled = false;
- mLocationManager.removeUpdates(mEmptyLocationListener);
-
- // Fall through to re-register listener.
- case MSG_ENABLE_LOCATION_UPDATES:
- // enable network provider to receive at least location updates for a given
- // distance.
- boolean networkLocationEnabled;
- try {
- networkLocationEnabled =
- mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
- } catch (Exception e) {
- // we may get IllegalArgumentException if network location provider
- // does not exist or is not yet installed.
- networkLocationEnabled = false;
- }
- if (!mNetworkListenerEnabled && networkLocationEnabled) {
- mNetworkListenerEnabled = true;
- mLastNetworkRegisterTime = SystemClock.elapsedRealtime();
- mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
- LOCATION_UPDATE_MS, 0, mEmptyLocationListener);
-
- if (!mDidFirstInit) {
- mDidFirstInit = true;
- if (mLocation == null) {
- retrieveLocation();
- }
- }
- }
-
- // enable passive provider to receive updates from location fixes (gps
- // and network).
- boolean passiveLocationEnabled;
- try {
- passiveLocationEnabled =
- mLocationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER);
- } catch (Exception e) {
- // we may get IllegalArgumentException if passive location provider
- // does not exist or is not yet installed.
- passiveLocationEnabled = false;
- }
-
- if (!mPassiveListenerEnabled && passiveLocationEnabled) {
- mPassiveListenerEnabled = true;
- mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
- 0, LOCATION_UPDATE_DISTANCE_METER , mLocationListener);
- }
-
- if (!(mNetworkListenerEnabled && mPassiveListenerEnabled)) {
- mLastUpdateInterval *= 1.5;
- if (mLastUpdateInterval == 0) {
- mLastUpdateInterval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN;
- } else if (mLastUpdateInterval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) {
- mLastUpdateInterval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX;
- }
- sendEmptyMessageDelayed(MSG_ENABLE_LOCATION_UPDATES, mLastUpdateInterval);
- }
- break;
-
- case MSG_DISABLE_LOCATION_UPDATES:
- mLocationManager.removeUpdates(mLocationListener);
- removeMessages(MSG_ENABLE_LOCATION_UPDATES);
- break;
-
- case MSG_DO_TWILIGHT_UPDATE:
+ // Update whenever the system clock is changed.
+ if (mTimeChangedReceiver == null) {
+ mTimeChangedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) Slog.d(TAG, "onReceive: " + intent);
updateTwilightState();
- break;
+ }
+ };
+
+ final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED);
+ intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
+ }
+
+ // Force an update now that we have listeners registered.
+ updateTwilightState();
+ }
+
+ private void stopListening() {
+ if (DEBUG) Slog.d(TAG, "stopListening");
+
+ if (mTimeChangedReceiver != null) {
+ getContext().unregisterReceiver(mTimeChangedReceiver);
+ mTimeChangedReceiver = null;
+ }
+
+ if (mLastTwilightState != null) {
+ mAlarmManager.cancel(this);
+ }
+
+ mLocationManager.removeUpdates(this);
+ mLastLocation = null;
+ }
+
+ private void updateTwilightState() {
+ // Calculate the twilight state based on the current time and location.
+ final long currentTimeMillis = System.currentTimeMillis();
+ final Location location = mLastLocation != null ? mLastLocation
+ : mLocationManager.getLastLocation();
+ final TwilightState state = calculateTwilightState(location, currentTimeMillis);
+ if (DEBUG) {
+ Slog.d(TAG, "updateTwilightState: " + state);
+ }
+
+ // Notify listeners if the state has changed.
+ synchronized (mListeners) {
+ if (!Objects.equals(mLastTwilightState, state)) {
+ mLastTwilightState = state;
+
+ for (int i = mListeners.size() - 1; i >= 0; --i) {
+ final TwilightListener listener = mListeners.keyAt(i);
+ final Handler handler = mListeners.valueAt(i);
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ listener.onTwilightStateChanged(state);
+ }
+ });
+ }
}
}
- private void retrieveLocation() {
- Location location = null;
- final Iterator<String> providers =
- mLocationManager.getProviders(new Criteria(), true).iterator();
- while (providers.hasNext()) {
- final Location lastKnownLocation =
- mLocationManager.getLastKnownLocation(providers.next());
- // pick the most recent location
- if (location == null || (lastKnownLocation != null &&
- location.getElapsedRealtimeNanos() <
- lastKnownLocation.getElapsedRealtimeNanos())) {
- location = lastKnownLocation;
- }
- }
-
- // In the case there is no location available (e.g. GPS fix or network location
- // is not available yet), the longitude of the location is estimated using the timezone,
- // latitude and accuracy are set to get a good average.
- if (location == null) {
- Time currentTime = new Time();
- currentTime.set(System.currentTimeMillis());
- double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE *
- (currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0));
- location = new Location("fake");
- location.setLongitude(lngOffset);
- location.setLatitude(0);
- location.setAccuracy(417000.0f);
- location.setTime(System.currentTimeMillis());
- location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
-
- if (DEBUG) {
- Slog.d(TAG, "Estimated location from timezone: " + location);
- }
- }
-
- setLocation(location);
- }
-
- private void setLocation(Location location) {
- mLocation = location;
- updateTwilightState();
- }
-
- private void updateTwilightState() {
- if (mLocation == null) {
- setTwilightState(null);
- return;
- }
-
- final long now = System.currentTimeMillis();
-
- // calculate today's twilight
- mTwilightCalculator.calculateTwilight(now,
- mLocation.getLatitude(), mLocation.getLongitude());
- final boolean isNight = (mTwilightCalculator.mState == TwilightCalculator.NIGHT);
- final long todaySunrise = mTwilightCalculator.mSunrise;
- final long todaySunset = mTwilightCalculator.mSunset;
-
- // calculate tomorrow's twilight
- mTwilightCalculator.calculateTwilight(now + DateUtils.DAY_IN_MILLIS,
- mLocation.getLatitude(), mLocation.getLongitude());
- final long tomorrowSunrise = mTwilightCalculator.mSunrise;
-
- float amount = 0;
- if (isNight) {
- if (todaySunrise == -1 || todaySunset == -1) {
- amount = 1;
- } else if (now > todaySunset) {
- amount = Math.min(1, (now - todaySunset) / (float) TWILIGHT_ADJUSTMENT_TIME);
- } else {
- amount = Math.max(0, 1
- - (todaySunrise - now) / (float) TWILIGHT_ADJUSTMENT_TIME);
- }
- }
- // set twilight state
- TwilightState state = new TwilightState(isNight, amount);
- if (DEBUG) {
- Slog.d(TAG, "Updating twilight state: " + state);
- }
- setTwilightState(state);
-
- // schedule next update
- long nextUpdate = 0;
- if (todaySunrise == -1 || todaySunset == -1) {
- // In the case the day or night never ends the update is scheduled 12 hours later.
- nextUpdate = now + 12 * DateUtils.HOUR_IN_MILLIS;
- } else {
- // add some extra time to be on the safe side.
- nextUpdate += DateUtils.MINUTE_IN_MILLIS;
-
- if (amount == 1 || amount == 0) {
- if (now > todaySunset) {
- nextUpdate += tomorrowSunrise;
- } else if (now > todaySunrise) {
- nextUpdate += todaySunset;
- } else {
- nextUpdate += todaySunrise;
- }
- } else {
- // This is the update rate while transitioning.
- // Leave at 10 min for now (one from above).
- nextUpdate += 9 * DateUtils.MINUTE_IN_MILLIS;
- }
- }
-
- if (DEBUG) {
- Slog.d(TAG, "Next update in " + (nextUpdate - now) + " ms");
- }
-
- Intent updateIntent = new Intent(ACTION_UPDATE_TWILIGHT_STATE);
- PendingIntent pendingIntent = PendingIntent.getBroadcast(
- getContext(), 0, updateIntent, 0);
- mAlarmManager.cancel(pendingIntent);
- mAlarmManager.setExact(AlarmManager.RTC, nextUpdate, pendingIntent);
+ // Schedule an alarm to update the state at the next sunrise or sunset.
+ if (state != null) {
+ final long triggerAtMillis = state.isNight()
+ ? state.sunriseTimeMillis() : state.sunsetTimeMillis();
+ mAlarmManager.setExact(AlarmManager.RTC, triggerAtMillis, TAG, this, mHandler);
}
}
- private final ContentObserver mContentObserver = new ContentObserver(new Handler()) {
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
- int value = Secure.getIntForUser(getContext().getContentResolver(),
- Secure.TWILIGHT_MODE, Secure.TWILIGHT_MODE_LOCKED_OFF, mCurrentUser);
- if (value == Secure.TWILIGHT_MODE_LOCKED_OFF) {
- setLockedState(new TwilightState(false, 0));
- } else if (value == Secure.TWILIGHT_MODE_LOCKED_ON) {
- setLockedState(new TwilightState(true, 1));
- } else if (value == Secure.TWILIGHT_MODE_AUTO_OVERRIDE_OFF) {
- setLockedState(new TwilightState(false, 0));
- scheduleReset();
- } else if (value == Secure.TWILIGHT_MODE_AUTO_OVERRIDE_ON) {
- setLockedState(new TwilightState(true, 1));
- scheduleReset();
- } else {
- mLocked = false;
- mLocationHandler.requestTwilightUpdate();
- }
- }
- };
+ @Override
+ public void onAlarm() {
+ if (DEBUG) Slog.d(TAG, "onAlarm");
+ updateTwilightState();
+ }
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
- mCurrentUser = ActivityManager.getCurrentUser();
- reregisterSettingObserver();
- return;
- }
- if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())
- && !intent.getBooleanExtra("state", false)) {
- // Airplane mode is now off!
- mLocationHandler.requestLocationUpdate();
- return;
- }
+ @Override
+ public void onLocationChanged(Location location) {
+ if (DEBUG) Slog.d(TAG, "onLocationChanged: " + location);
+ mLastLocation = location;
+ updateTwilightState();
+ }
- if (ACTION_RESET_TWILIGHT_AUTO.equals(intent.getAction())) {
- int user = intent.getIntExtra(EXTRA_RESET_USER, 0);
- Settings.Secure.putIntForUser(getContext().getContentResolver(),
- Secure.TWILIGHT_MODE, Secure.TWILIGHT_MODE_AUTO, user);
- return;
- }
- // Time zone has changed or alarm expired.
- mLocationHandler.requestTwilightUpdate();
- }
- };
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
- // A LocationListener to initialize the network location provider. The location updates
- // are handled through the passive location provider.
- private final LocationListener mEmptyLocationListener = new LocationListener() {
- public void onLocationChanged(Location location) {
+ @Override
+ public void onProviderEnabled(String provider) {
+ }
+
+ @Override
+ public void onProviderDisabled(String provider) {
+ }
+
+ /**
+ * Calculates the twilight state for a specific location and time.
+ *
+ * @param location the location to use
+ * @param timeMillis the reference time to use
+ * @return the calculated {@link TwilightState}, or {@code null} if location is {@code null}
+ */
+ private static TwilightState calculateTwilightState(Location location, long timeMillis) {
+ if (location == null) {
+ return null;
}
- public void onProviderDisabled(String provider) {
+ final CalendarAstronomer ca = new CalendarAstronomer(
+ location.getLongitude(), location.getLatitude());
+
+ final Calendar noon = Calendar.getInstance();
+ noon.setTimeInMillis(timeMillis);
+ noon.set(Calendar.HOUR_OF_DAY, 12);
+ noon.set(Calendar.MINUTE, 0);
+ noon.set(Calendar.SECOND, 0);
+ noon.set(Calendar.MILLISECOND, 0);
+ ca.setTime(noon.getTimeInMillis());
+
+ long sunriseTimeMillis = ca.getSunRiseSet(true /* rise */);
+ long sunsetTimeMillis = ca.getSunRiseSet(false /* rise */);
+
+ if (sunsetTimeMillis < timeMillis) {
+ noon.add(Calendar.DATE, 1);
+ ca.setTime(noon.getTimeInMillis());
+ sunriseTimeMillis = ca.getSunRiseSet(true /* rise */);
+ } else if (sunriseTimeMillis > timeMillis) {
+ noon.add(Calendar.DATE, -1);
+ ca.setTime(noon.getTimeInMillis());
+ sunsetTimeMillis = ca.getSunRiseSet(false /* rise */);
}
- public void onProviderEnabled(String provider) {
- }
-
- public void onStatusChanged(String provider, int status, Bundle extras) {
- }
- };
-
- private final LocationListener mLocationListener = new LocationListener() {
- public void onLocationChanged(Location location) {
- mLocationHandler.processNewLocation(location);
- }
-
- public void onProviderDisabled(String provider) {
- }
-
- public void onProviderEnabled(String provider) {
- }
-
- public void onStatusChanged(String provider, int status, Bundle extras) {
- }
- };
+ return new TwilightState(sunriseTimeMillis, sunsetTimeMillis);
+ }
}
diff --git a/services/core/java/com/android/server/twilight/TwilightState.java b/services/core/java/com/android/server/twilight/TwilightState.java
index 81abc13..a12965d 100644
--- a/services/core/java/com/android/server/twilight/TwilightState.java
+++ b/services/core/java/com/android/server/twilight/TwilightState.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * 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.
@@ -16,58 +16,89 @@
package com.android.server.twilight;
-import java.text.DateFormat;
-import java.util.Date;
+import android.text.format.DateFormat;
+
+import java.util.Calendar;
/**
- * Describes whether it is day or night.
- * This object is immutable.
+ * The twilight state, consisting of the sunrise and sunset times (in millis) for the current
+ * period.
+ * <p/>
+ * Note: This object is immutable.
*/
-public class TwilightState {
- private final boolean mIsNight;
- private final float mAmount;
+public final class TwilightState {
- TwilightState(boolean isNight, float amount) {
- mIsNight = isNight;
- mAmount = amount;
+ private final long mSunriseTimeMillis;
+ private final long mSunsetTimeMillis;
+
+ TwilightState(long sunriseTimeMillis, long sunsetTimeMillis) {
+ mSunriseTimeMillis = sunriseTimeMillis;
+ mSunsetTimeMillis = sunsetTimeMillis;
}
/**
- * Returns true if it is currently night time.
+ * Returns the time (in UTC milliseconds from epoch) of the upcoming or previous sunrise if
+ * it's night or day respectively.
+ */
+ public long sunriseTimeMillis() {
+ return mSunriseTimeMillis;
+ }
+
+ /**
+ * Returns a new {@link Calendar} instance initialized to {@link #sunriseTimeMillis()}.
+ */
+ public Calendar sunrise() {
+ final Calendar sunrise = Calendar.getInstance();
+ sunrise.setTimeInMillis(mSunriseTimeMillis);
+ return sunrise;
+ }
+
+ /**
+ * Returns the time (in UTC milliseconds from epoch) of the upcoming or previous sunset if
+ * it's day or night respectively.
+ */
+ public long sunsetTimeMillis() {
+ return mSunsetTimeMillis;
+ }
+
+ /**
+ * Returns a new {@link Calendar} instance initialized to {@link #sunsetTimeMillis()}.
+ */
+ public Calendar sunset() {
+ final Calendar sunset = Calendar.getInstance();
+ sunset.setTimeInMillis(mSunsetTimeMillis);
+ return sunset;
+ }
+
+ /**
+ * Returns {@code true} if it is currently night time.
*/
public boolean isNight() {
- return mIsNight;
- }
-
- /**
- * For twilight affects that change gradually over time, this is the amount they
- * should currently be in effect.
- */
- public float getAmount() {
- return mAmount;
+ final long now = System.currentTimeMillis();
+ return now >= mSunsetTimeMillis && now < mSunriseTimeMillis;
}
@Override
public boolean equals(Object o) {
- return o instanceof TwilightState && equals((TwilightState)o);
+ return o instanceof TwilightState && equals((TwilightState) o);
}
public boolean equals(TwilightState other) {
return other != null
- && mIsNight == other.mIsNight
- && mAmount == other.mAmount;
+ && mSunriseTimeMillis == other.mSunriseTimeMillis
+ && mSunsetTimeMillis == other.mSunsetTimeMillis;
}
@Override
public int hashCode() {
- return 0; // don't care
+ return Long.hashCode(mSunriseTimeMillis) ^ Long.hashCode(mSunsetTimeMillis);
}
@Override
public String toString() {
- DateFormat f = DateFormat.getDateTimeInstance();
- return "{TwilightState: isNight=" + mIsNight
- + ", mAmount=" + mAmount
- + "}";
+ return "TwilightState {"
+ + " sunrise=" + DateFormat.format("MM-dd HH:mm", mSunriseTimeMillis)
+ + " sunset="+ DateFormat.format("MM-dd HH:mm", mSunsetTimeMillis)
+ + " }";
}
}
diff --git a/services/core/java/com/android/server/vr/EnabledComponentsObserver.java b/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
index 77e8b1f..7126cb5 100644
--- a/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
+++ b/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
@@ -215,7 +215,11 @@
*/
public ArraySet<ComponentName> getInstalled(int userId) {
synchronized (mLock) {
- return mInstalledSet.get(userId);
+ ArraySet<ComponentName> ret = mInstalledSet.get(userId);
+ if (ret == null) {
+ return new ArraySet<ComponentName>();
+ }
+ return ret;
}
}
@@ -227,7 +231,12 @@
*/
public ArraySet<ComponentName> getEnabled(int userId) {
synchronized (mLock) {
- return mEnabledSet.get(userId);
+ ArraySet<ComponentName> ret = mEnabledSet.get(userId);
+ if (ret == null) {
+ return new ArraySet<ComponentName>();
+ }
+ return ret;
+
}
}
@@ -246,7 +255,9 @@
Intent queryIntent = new Intent(serviceName);
List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
queryIntent,
- PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
+ PackageManager.GET_SERVICES | PackageManager.GET_META_DATA |
+ PackageManager.MATCH_DIRECT_BOOT_AWARE |
+ PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
userId);
if (installedServices != null) {
for (int i = 0, count = installedServices.size(); i < count; i++) {
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 5fefd4c..fdadc8d 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -46,6 +46,7 @@
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.service.vr.VrListenerService;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -218,6 +219,7 @@
String packageName = mNotificationAccessPackageToUserId.keyAt(i);
revokeNotificationListenerAccess(packageName, grantUserId);
revokeNotificationPolicyAccess(packageName);
+ revokeCoarseLocationPermissionIfNeeded(packageName, grantUserId);
mNotificationAccessPackageToUserId.removeAt(i);
}
}
@@ -226,6 +228,7 @@
if (!packageNames.contains(pkg)) {
revokeNotificationListenerAccess(pkg, currentUserId);
revokeNotificationPolicyAccess(pkg);
+ revokeCoarseLocationPermissionIfNeeded(pkg, currentUserId);
mNotificationAccessPackageToUserId.remove(pkg);
}
}
@@ -233,6 +236,7 @@
if (!allowed.contains(pkg)) {
grantNotificationPolicyAccess(pkg);
grantNotificationListenerAccess(pkg, currentUserId);
+ grantCoarseLocationPermissionIfNeeded(pkg, currentUserId);
mNotificationAccessPackageToUserId.put(pkg, currentUserId);
}
}
@@ -403,107 +407,6 @@
publishLocalService(VrManagerInternal.class, new LocalService());
publishBinderService(VR_MANAGER_BINDER_SERVICE, mVrManager.asBinder());
-
- // If there are no VR packages installed on the device, then disable VR
- // components, otherwise, enable them.
- setEnabledStatusOfVrComponents();
- }
-
- private void setEnabledStatusOfVrComponents() {
- ArraySet<ComponentName> vrComponents = SystemConfig.getInstance().getDefaultVrComponents();
- if (vrComponents == null) {
- return;
- }
-
- // We only want to enable VR components if there is a VR package installed on the device.
- // The VR components themselves do not quality as a VR package, so exclude them.
- ArraySet<String> vrComponentPackageNames = new ArraySet<>();
- for (ComponentName componentName : vrComponents) {
- vrComponentPackageNames.add(componentName.getPackageName());
- }
-
- // Check to see if there are any packages on the device, other than the VR component
- // packages.
- PackageManager pm = mContext.getPackageManager();
- List<PackageInfo> packageInfos = pm.getInstalledPackages(
- PackageManager.GET_CONFIGURATIONS);
- boolean vrModeIsUsed = false;
- for (PackageInfo packageInfo : packageInfos) {
- if (packageInfo != null && packageInfo.packageName != null &&
- pm.getApplicationEnabledSetting(packageInfo.packageName) ==
- PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
- vrModeIsUsed = enableVrComponentsIfVrModeUsed(pm, packageInfo,
- vrComponentPackageNames, vrComponents);
- if (vrModeIsUsed) {
- break;
- }
- }
- }
-
- if (!vrModeIsUsed) {
- Slog.i(TAG, "No VR packages found, disabling VR components");
- setVrComponentsEnabledOrDisabled(vrComponents, false);
-
- // Register to receive an intent when a new package is installed, in case that package
- // requires VR components.
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- intentFilter.addDataScheme("package");
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- PackageManager pm = context.getPackageManager();
- final String packageName = intent.getData().getSchemeSpecificPart();
- if (packageName != null) {
- try {
- PackageInfo packageInfo = pm.getPackageInfo(packageName,
- PackageManager.GET_CONFIGURATIONS);
- enableVrComponentsIfVrModeUsed(pm, packageInfo,
- vrComponentPackageNames, vrComponents);
- } catch (NameNotFoundException e) {
- }
- }
- };
- }, intentFilter);
- }
- }
-
- private void setVrComponentsEnabledOrDisabled(ArraySet<ComponentName> vrComponents,
- boolean enabled) {
- int state = enabled ?
- PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
- PackageManager pm = mContext.getPackageManager();
- for (ComponentName componentName : vrComponents) {
- try {
- // Note that we must first check for the existance of the package before trying
- // to set its enabled state. This is to prevent PackageManager from throwing
- // an excepton if the package is not found (not just a NameNotFoundException
- // exception).
- PackageInfo packageInfo = pm.getPackageInfo(componentName.getPackageName(),
- PackageManager.GET_CONFIGURATIONS);
- pm.setApplicationEnabledSetting(componentName.getPackageName(), state , 0);
- } catch (NameNotFoundException e) {
- }
- }
- }
-
- private boolean enableVrComponentsIfVrModeUsed(PackageManager pm, PackageInfo packageInfo,
- ArraySet<String> vrComponentPackageNames, ArraySet<ComponentName> vrComponents) {
- boolean isVrComponent = vrComponents != null &&
- vrComponentPackageNames.contains(packageInfo.packageName);
- if (packageInfo != null && packageInfo.reqFeatures != null && !isVrComponent) {
- for (FeatureInfo featureInfo : packageInfo.reqFeatures) {
- if (featureInfo.name != null &&
- (featureInfo.name.equals(PackageManager.FEATURE_VR_MODE) ||
- featureInfo.name.equals(PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE))) {
- Slog.i(TAG, "VR package found, enabling VR components");
- setVrComponentsEnabledOrDisabled(vrComponents, true);
- return true;
- }
- }
- }
- return false;
}
@Override
@@ -744,7 +647,7 @@
for (String c : current) {
ComponentName component = ComponentName.unflattenFromString(c);
- if (component.getPackageName().equals(pkg)) {
+ if (component != null && component.getPackageName().equals(pkg)) {
toRemove.add(c);
}
}
@@ -757,6 +660,22 @@
flatSettings, userId);
}
+ private void grantCoarseLocationPermissionIfNeeded(String pkg, int userId) {
+ // Don't clobber the user if permission set in current state explicitly
+ if (!isPermissionUserUpdated(Manifest.permission.ACCESS_COARSE_LOCATION, pkg, userId)) {
+ mContext.getPackageManager().grantRuntimePermission(pkg,
+ Manifest.permission.ACCESS_COARSE_LOCATION, new UserHandle(userId));
+ }
+ }
+
+ private void revokeCoarseLocationPermissionIfNeeded(String pkg, int userId) {
+ // Don't clobber the user if permission set in current state explicitly
+ if (!isPermissionUserUpdated(Manifest.permission.ACCESS_COARSE_LOCATION, pkg, userId)) {
+ mContext.getPackageManager().revokeRuntimePermission(pkg,
+ Manifest.permission.ACCESS_COARSE_LOCATION, new UserHandle(userId));
+ }
+ }
+
private boolean isPermissionUserUpdated(String permission, String pkg, int userId) {
final int flags = mContext.getPackageManager().getPermissionFlags(
permission, pkg, new UserHandle(userId));
@@ -772,7 +691,9 @@
if (flat != null) {
String[] allowed = flat.split(":");
for (String s : allowed) {
- current.add(s);
+ if (!TextUtils.isEmpty(s)) {
+ current.add(s);
+ }
}
}
return current;
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 8137c4e..3fd4b37 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -85,6 +85,7 @@
import com.android.internal.R;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.JournaledFile;
import com.android.server.EventLogTags;
@@ -166,7 +167,7 @@
* Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks
* that the wallpaper has changed. The CREATE is triggered when there is no
* wallpaper set and is created for the first time. The CLOSE_WRITE is triggered
- * everytime the wallpaper is changed.
+ * every time the wallpaper is changed.
*/
private class WallpaperObserver extends FileObserver {
@@ -175,7 +176,6 @@
final File mWallpaperDir;
final File mWallpaperFile;
final File mWallpaperLockFile;
- final File mWallpaperInfoFile;
public WallpaperObserver(WallpaperData wallpaper) {
super(getWallpaperDir(wallpaper.userId).getAbsolutePath(),
@@ -185,7 +185,6 @@
mWallpaper = wallpaper;
mWallpaperFile = new File(mWallpaperDir, WALLPAPER);
mWallpaperLockFile = new File(mWallpaperDir, WALLPAPER_LOCK_ORIG);
- mWallpaperInfoFile = new File(mWallpaperDir, WALLPAPER_INFO);
}
private WallpaperData dataForEvent(boolean sysChanged, boolean lockChanged) {
@@ -253,6 +252,7 @@
if (DEBUG) {
Slog.v(TAG, "Wallpaper written; generating crop");
}
+ SELinux.restorecon(changedFile);
if (moved) {
// This is a restore, so generate the crop using any just-restored new
// crop guidelines, making sure to preserve our local dimension hints.
@@ -260,7 +260,6 @@
if (DEBUG) {
Slog.v(TAG, "moved-to, therefore restore; reloading metadata");
}
- SELinux.restorecon(changedFile);
loadSettingsLocked(wallpaper.userId, true);
}
generateCrop(wallpaper);
@@ -354,8 +353,8 @@
(cropHint.bottom > options.outHeight ? options.outHeight - cropHint.bottom : 0));
// Don't bother cropping if what we're left with is identity
- needCrop = (options.outHeight >= cropHint.height()
- && options.outWidth >= cropHint.width());
+ needCrop = (options.outHeight > cropHint.height()
+ && options.outWidth > cropHint.width());
}
// scale if the crop height winds up not matching the recommended metrics
@@ -488,6 +487,7 @@
final SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<WallpaperData>();
+ final SparseArray<Boolean> mUserRestorecon = new SparseArray<Boolean>();
int mCurrentUserId;
static class WallpaperData {
@@ -910,10 +910,9 @@
public void onForegroundProfileSwitch(int newProfileId) {
// Ignore.
}
- });
+ }, TAG);
} catch (RemoteException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ e.rethrowAsRuntimeException();
}
}
@@ -944,10 +943,34 @@
mLockWallpaperMap.remove(userId);
}
- void onUnlockUser(int userId) {
+ void onUnlockUser(final int userId) {
synchronized (mLock) {
- if (mCurrentUserId == userId && mWaitingForUnlock) {
- switchUser(userId, null);
+ if (mCurrentUserId == userId) {
+ if (mWaitingForUnlock) {
+ // If we're switching users, now is when we transition the wallpaper
+ switchUser(userId, null);
+ }
+
+ // Make sure that the SELinux labeling of all the relevant files is correct.
+ // This corrects for mislabeling bugs that might have arisen from move-to
+ // operations involving the wallpaper files. This isn't timing-critical,
+ // so we do it in the background to avoid holding up the user unlock operation.
+ if (mUserRestorecon.get(userId) != Boolean.TRUE) {
+ mUserRestorecon.put(userId, Boolean.TRUE);
+ Runnable relabeler = new Runnable() {
+ @Override
+ public void run() {
+ final File wallpaperDir = getWallpaperDir(userId);
+ for (String filename : sPerUserFiles) {
+ File f = new File(wallpaperDir, filename);
+ if (f.exists()) {
+ SELinux.restorecon(f);
+ }
+ }
+ }
+ };
+ BackgroundThread.getHandler().post(relabeler);
+ }
}
}
}
@@ -1283,8 +1306,9 @@
}
@Override
- public WallpaperInfo getWallpaperInfo() {
- int userId = UserHandle.getCallingUserId();
+ public WallpaperInfo getWallpaperInfo(int userId) {
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, true, "getWallpaperIdForUser", null);
synchronized (mLock) {
WallpaperData wallpaper = mWallpaperMap.get(userId);
if (wallpaper != null && wallpaper.connection != null) {
@@ -1326,7 +1350,9 @@
@Override
public ParcelFileDescriptor setWallpaper(String name, String callingPackage,
Rect cropHint, boolean allowBackup, Bundle extras, int which,
- IWallpaperManagerCallback completion) {
+ IWallpaperManagerCallback completion, int userId) {
+ userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
+ false /* all */, true /* full */, "changing wallpaper", null /* pkg */);
checkPermission(android.Manifest.permission.SET_WALLPAPER);
if ((which & (FLAG_LOCK|FLAG_SYSTEM)) == 0) {
@@ -1350,8 +1376,6 @@
}
}
- final int userId = UserHandle.getCallingUserId();
-
synchronized (mLock) {
if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which));
WallpaperData wallpaper;
@@ -1376,9 +1400,7 @@
wallpaper.whichPending = which;
wallpaper.setComplete = completion;
wallpaper.cropHint.set(cropHint);
- if ((which & FLAG_SYSTEM) != 0) {
- wallpaper.allowBackup = allowBackup;
- }
+ wallpaper.allowBackup = allowBackup;
}
return pfd;
} finally {
@@ -1453,19 +1475,27 @@
}
@Override
- public void setWallpaperComponentChecked(ComponentName name, String callingPackage) {
+ public void setWallpaperComponentChecked(ComponentName name, String callingPackage,
+ int userId) {
+
if (isWallpaperSupported(callingPackage) && isSetWallpaperAllowed(callingPackage)) {
- setWallpaperComponent(name);
+ setWallpaperComponent(name, userId);
}
}
// ToDo: Remove this version of the function
@Override
public void setWallpaperComponent(ComponentName name) {
+ setWallpaperComponent(name, UserHandle.getCallingUserId());
+ }
+
+ private void setWallpaperComponent(ComponentName name, int userId) {
+ userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
+ false /* all */, true /* full */, "changing live wallpaper", null /* pkg */);
checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
+
synchronized (mLock) {
if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);
- int userId = UserHandle.getCallingUserId();
WallpaperData wallpaper = mWallpaperMap.get(userId);
if (wallpaper == null) {
throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
@@ -1721,12 +1751,14 @@
}
@Override
- public boolean isWallpaperBackupEligible(int userId) {
+ public boolean isWallpaperBackupEligible(int which, int userId) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Only the system may call isWallpaperBackupEligible");
}
- WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
+ WallpaperData wallpaper = (which == FLAG_LOCK)
+ ? mLockWallpaperMap.get(userId)
+ : mWallpaperMap.get(userId);
return (wallpaper != null) ? wallpaper.allowBackup : false;
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 8be5dfb..e5e2175 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -348,6 +348,7 @@
}
switch (type) {
case WindowManager.LayoutParams.TYPE_APPLICATION:
+ case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION:
case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
@@ -1071,14 +1072,15 @@
Region.Op.REVERSE_DIFFERENCE);
}
- // We figured out what is touchable for the entire screen - done.
- if (unaccountedSpace.isEmpty()) {
- break;
- }
-
// If a window is modal it prevents other windows from being touched
if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
+ // Account for all space in the task, whether the windows in it are
+ // touchable or not. The modal window blocks all touches from the task's
+ // area.
+ unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
+ Region.Op.REVERSE_DIFFERENCE);
+
if (task != null) {
// If the window is associated with a particular task, we can skip the
// rest of the windows for that task.
@@ -1090,6 +1092,10 @@
break;
}
}
+ // We figured out what is touchable for the entire screen - done.
+ if (unaccountedSpace.isEmpty()) {
+ break;
+ }
}
// Always report the focused window.
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 4553f8e..d4d6f32 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -162,6 +162,9 @@
private final WindowManagerService mService;
private int mNextAppTransition = TRANSIT_UNSET;
+ private int mLastUsedAppTransition = TRANSIT_UNSET;
+ private String mLastOpeningApp;
+ private String mLastClosingApp;
private static final int NEXT_TRANSIT_TYPE_NONE = 0;
private static final int NEXT_TRANSIT_TYPE_CUSTOM = 1;
@@ -285,6 +288,13 @@
private void setAppTransition(int transit) {
mNextAppTransition = transit;
+ setLastAppTransition(TRANSIT_UNSET, null, null);
+ }
+
+ void setLastAppTransition(int transit, AppWindowToken openingApp, AppWindowToken closingApp) {
+ mLastUsedAppTransition = transit;
+ mLastOpeningApp = "" + openingApp;
+ mLastClosingApp = "" + closingApp;
}
boolean isReady() {
@@ -604,7 +614,7 @@
float scaleH = mTmpRect.height() / (float) appHeight;
Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1,
computePivot(mTmpRect.left, scaleW),
- computePivot(mTmpRect.right, scaleH));
+ computePivot(mTmpRect.top, scaleH));
scale.setInterpolator(mDecelerateInterpolator);
Animation alpha = new AlphaAnimation(0, 1);
@@ -1057,7 +1067,7 @@
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
final int thumbHeightI = mTmpRect.height();
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
- final int thumbStartX = mTmpRect.left - containingFrame.left;
+ final int thumbStartX = mTmpRect.left - containingFrame.left - contentInsets.left;
final int thumbStartY = mTmpRect.top - containingFrame.top;
switch (thumbTransitState) {
@@ -1615,8 +1625,7 @@
if (isTransitionSet()) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_SCALE_UP;
- putDefaultNextAppTransitionCoordinates(startX, startY, startX + startWidth,
- startY + startHeight, null);
+ putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight, null);
postAnimationCallback();
}
}
@@ -1905,6 +1914,14 @@
pw.print(prefix); pw.print("mNextAppTransitionCallback=");
pw.println(mNextAppTransitionCallback);
}
+ if (mLastUsedAppTransition != TRANSIT_NONE) {
+ pw.print(prefix); pw.print("mLastUsedAppTransition=");
+ pw.println(appTransitionToString(mLastUsedAppTransition));
+ pw.print(prefix); pw.print("mLastOpeningApp=");
+ pw.println(mLastOpeningApp);
+ pw.print(prefix); pw.print("mLastClosingApp=");
+ pw.println(mLastClosingApp);
+ }
}
public void setCurrentUser(int newUserId) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index e425e7d1..621e43a 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -17,7 +17,9 @@
package com.android.server.wm;
import static android.app.ActivityManager.StackId;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
@@ -132,6 +134,7 @@
boolean mAlwaysFocusable;
boolean mAppStopped;
+ int mRotationAnimationHint;
int mPendingRelaunchCount;
private ArrayList<WindowSurfaceController.SurfaceControlWithBackground> mSurfaceViewBackgrounds =
@@ -336,16 +339,66 @@
}
}
- // Here we destroy surfaces which have been marked as eligible by the animator, taking care
- // to ensure the client has finished with them. If the client could still be using them
- // we will skip destruction and try again when the client has stopped.
+ void clearAnimatingFlags() {
+ boolean wallpaperMightChange = false;
+ for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+ final WindowState win = allAppWindows.get(i);
+ // We don't want to clear it out for windows that get replaced, because the
+ // animation depends on the flag to remove the replaced window.
+ //
+ // We also don't clear the mAnimatingExit flag for windows which have the
+ // mRemoveOnExit flag. This indicates an explicit remove request has been issued
+ // by the client. We should let animation proceed and not clear this flag or
+ // they won't eventually be removed by WindowStateAnimator#finishExit.
+ if (!win.mWillReplaceWindow && !win.mRemoveOnExit) {
+ // Clear mAnimating flag together with mAnimatingExit. When animation
+ // changes from exiting to entering, we need to clear this flag until the
+ // new animation gets applied, so that isAnimationStarting() becomes true
+ // until then.
+ // Otherwise applySurfaceChangesTransaction will faill to skip surface
+ // placement for this window during this period, one or more frame will
+ // show up with wrong position or scale.
+ if (win.mAnimatingExit) {
+ win.mAnimatingExit = false;
+ wallpaperMightChange = true;
+ }
+ if (win.mWinAnimator.mAnimating) {
+ win.mWinAnimator.mAnimating = false;
+ wallpaperMightChange = true;
+ }
+ if (win.mDestroying) {
+ win.mDestroying = false;
+ service.mDestroySurface.remove(win);
+ wallpaperMightChange = true;
+ }
+ }
+ }
+ if (wallpaperMightChange) {
+ requestUpdateWallpaperIfNeeded();
+ }
+ }
+
void destroySurfaces() {
+ destroySurfaces(false /*cleanupOnResume*/);
+ }
+
+ /**
+ * Destroy surfaces which have been marked as eligible by the animator, taking care to ensure
+ * the client has finished with them.
+ *
+ * @param cleanupOnResume whether this is done when app is resumed without fully stopped. If
+ * set to true, destroy only surfaces of removed windows, and clear relevant flags of the
+ * others so that they are ready to be reused. If set to false (common case), destroy all
+ * surfaces that's eligible, if the app is already stopped.
+ */
+
+ private void destroySurfaces(boolean cleanupOnResume) {
final ArrayList<WindowState> allWindows = (ArrayList<WindowState>) allAppWindows.clone();
final DisplayContentList displayList = new DisplayContentList();
for (int i = allWindows.size() - 1; i >= 0; i--) {
final WindowState win = allWindows.get(i);
- if (!(mAppStopped || win.mWindowRemovalAllowed)) {
+ if (!(mAppStopped || win.mWindowRemovalAllowed || cleanupOnResume)) {
continue;
}
@@ -360,7 +413,9 @@
+ " win.mWindowRemovalAllowed=" + win.mWindowRemovalAllowed
+ " win.mRemoveOnExit=" + win.mRemoveOnExit);
- win.destroyOrSaveSurface();
+ if (!cleanupOnResume || win.mRemoveOnExit) {
+ win.destroyOrSaveSurface();
+ }
if (win.mRemoveOnExit) {
service.removeWindowInnerLocked(win);
}
@@ -368,6 +423,9 @@
if (displayContent != null && !displayList.contains(displayContent)) {
displayList.add(displayContent);
}
+ if (cleanupOnResume) {
+ win.requestUpdateWallpaperIfNeeded();
+ }
win.mDestroying = false;
}
for (int i = 0; i < displayList.size(); i++) {
@@ -378,18 +436,31 @@
}
/**
- * If the application has stopped it is okay to destroy any surfaces which were keeping alive
- * in case they were still being used.
+ * Notify that the app is now resumed, and it was not stopped before, perform a clean
+ * up of the surfaces
*/
- void notifyAppStopped(boolean stopped) {
- if (DEBUG_ADD_REMOVE) Slog.v(TAG, "notifyAppStopped: stopped=" + stopped + " " + this);
- mAppStopped = stopped;
-
- if (stopped) {
- destroySurfaces();
- // Remove any starting window that was added for this app if they are still around.
- mTask.mService.scheduleRemoveStartingWindowLocked(this);
+ void notifyAppResumed(boolean wasStopped, boolean allowSavedSurface) {
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG, "notifyAppResumed: wasStopped=" + wasStopped
+ + " allowSavedSurface=" + allowSavedSurface + " " + this);
+ mAppStopped = false;
+ if (!wasStopped) {
+ destroySurfaces(true /*cleanupOnResume*/);
}
+ if (!allowSavedSurface) {
+ destroySavedSurfaces();
+ }
+ }
+
+ /**
+ * Notify that the app has stopped, and it is okay to destroy any surfaces which were
+ * keeping alive in case they were still being used.
+ */
+ void notifyAppStopped() {
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG, "notifyAppStopped: " + this);
+ mAppStopped = true;
+ destroySurfaces();
+ // Remove any starting window that was added for this app if they are still around.
+ mTask.mService.scheduleRemoveStartingWindowLocked(this);
}
/**
@@ -628,6 +699,16 @@
}
}
+ void clearRelaunching() {
+ if (mPendingRelaunchCount == 0) {
+ return;
+ }
+ if (canFreezeBounds()) {
+ unfreezeBounds();
+ }
+ mPendingRelaunchCount = 0;
+ }
+
void addWindow(WindowState w) {
for (int i = allAppWindows.size() - 1; i >= 0; i--) {
WindowState candidate = allAppWindows.get(i);
@@ -707,8 +788,12 @@
* Unfreezes the previously frozen bounds. See {@link #freezeBounds}.
*/
private void unfreezeBounds() {
- mFrozenBounds.remove();
- mFrozenMergedConfig.remove();
+ if (!mFrozenBounds.isEmpty()) {
+ mFrozenBounds.remove();
+ }
+ if (!mFrozenMergedConfig.isEmpty()) {
+ mFrozenMergedConfig.remove();
+ }
for (int i = windows.size() - 1; i >= 0; i--) {
final WindowState win = windows.get(i);
if (!win.mHasSurface) {
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
index 2b9879e..7f97c46 100644
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -1,5 +1,6 @@
package com.android.server.wm;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DIM_LAYER;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -250,6 +251,13 @@
duration = getDimLayerFadeDuration(duration);
}
state.dimLayer.show(dimLayer, dimAmount, duration);
+
+ // If we showed a dim layer, make sure to redo the layout because some things depend
+ // on whether a dim layer is showing or not.
+ if (targetAlpha == 0) {
+ mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
+ mDisplayContent.layoutNeeded = true;
+ }
}
} else if (state.dimLayer.getLayer() != dimLayer) {
state.dimLayer.setLayer(dimLayer);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index fba439f..1d57872 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -191,6 +191,16 @@
return mHomeStack;
}
+ TaskStack getStackById(int stackId) {
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final TaskStack stack = mStacks.get(i);
+ if (stack.mStackId == stackId) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
void updateDisplayInfo() {
mDisplay.getDisplayInfo(mDisplayInfo);
mDisplay.getMetrics(mDisplayMetrics);
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index e73649d..f93e2ff 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -388,8 +388,8 @@
inputMethodManagerInternal.hideCurrentInputMethod();
mImeHideRequested = true;
}
- } else {
- setMinimizedDockedStack(false);
+ } else if (setMinimizedDockedStack(false)) {
+ mService.mWindowPlacerLocked.performSurfacePlacement();
}
}
@@ -542,31 +542,43 @@
return;
}
- clearImeAdjustAnimation();
+ final boolean imeChanged = clearImeAdjustAnimation();
+ boolean minimizedChange = false;
if (minimizedDock) {
if (animate) {
startAdjustAnimation(0f, 1f);
} else {
- setMinimizedDockedStack(true);
+ minimizedChange |= setMinimizedDockedStack(true);
}
} else {
if (animate) {
startAdjustAnimation(1f, 0f);
} else {
- setMinimizedDockedStack(false);
+ minimizedChange |= setMinimizedDockedStack(false);
}
}
+ if (imeChanged || minimizedChange) {
+ if (imeChanged && !minimizedChange) {
+ Slog.d(TAG, "setMinimizedDockedStack: IME adjust changed due to minimizing,"
+ + " minimizedDock=" + minimizedDock
+ + " minimizedChange=" + minimizedChange);
+ }
+ mService.mWindowPlacerLocked.performSurfacePlacement();
+ }
}
- private void clearImeAdjustAnimation() {
+ private boolean clearImeAdjustAnimation() {
+ boolean changed = false;
final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
for (int i = stacks.size() - 1; i >= 0; --i) {
final TaskStack stack = stacks.get(i);
if (stack != null && stack.isAdjustedForIme()) {
stack.resetAdjustedForIme(true /* adjustBoundsNow */);
+ changed = true;
}
}
mAnimatingForIme = false;
+ return changed;
}
private void startAdjustAnimation(float from, float to) {
@@ -625,8 +637,21 @@
if (mDelayedImeWin != null) {
mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
}
+ // If the adjust status changed since this was posted, only notify
+ // the new states and don't animate.
+ long duration = 0;
+ if (mAdjustedForIme == adjustedForIme
+ && mAdjustedForDivider == adjustedForDivider) {
+ duration = IME_ADJUST_ANIM_DURATION;
+ } else {
+ Slog.w(TAG, "IME adjust changed while waiting for drawn:"
+ + " adjustedForIme=" + adjustedForIme
+ + " adjustedForDivider=" + adjustedForDivider
+ + " mAdjustedForIme=" + mAdjustedForIme
+ + " mAdjustedForDivider=" + mAdjustedForDivider);
+ }
notifyAdjustedForImeChanged(
- adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION);
+ mAdjustedForIme || mAdjustedForDivider, duration);
};
} else {
notifyAdjustedForImeChanged(
@@ -634,15 +659,10 @@
}
}
- private void setMinimizedDockedStack(boolean minimized) {
+ private boolean setMinimizedDockedStack(boolean minimized) {
final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
notifyDockedStackMinimizedChanged(minimized, 0);
- if (stack == null) {
- return;
- }
- if (stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f)) {
- mService.mWindowPlacerLocked.performSurfacePlacement();
- }
+ return stack != null && stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f);
}
private boolean isAnimationMaximizing() {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b4387b9..1dcada6 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -624,8 +624,8 @@
//
// As we use this flag as a hint to freeze surface boundary updates,
// we'd like to only apply this to TYPE_BASE_APPLICATION,
- // windows of TYPE_APPLICATION like dialogs, could appear
- // to not be drag resizing while they resize, but we'd
+ // windows of TYPE_APPLICATION (or TYPE_DRAWN_APPLICATION) like dialogs,
+ // could appear to not be drag resizing while they resize, but we'd
// still like to manipulate their frame to update crop, etc...
//
// Anyway we don't need to synchronize position and content updates for these
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 88028be..2b66c3a 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -246,7 +246,10 @@
boolean updateWallpaperOffset(WindowState wallpaperWin, int dw, int dh, boolean sync) {
boolean rawChanged = false;
- float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : 0.5f;
+ // Set the default wallpaper x-offset to either edge of the screen (depending on RTL), to
+ // match the behavior of most Launchers
+ float defaultWallpaperX = wallpaperWin.isRtl() ? 1f : 0f;
+ float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : defaultWallpaperX;
float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f;
int availw = wallpaperWin.mFrame.right - wallpaperWin.mFrame.left - dw;
int offset = availw > 0 ? -(int)(availw * wpx + .5f) : 0;
@@ -479,6 +482,8 @@
boolean resetTopWallpaper = false;
boolean inFreeformSpace = false;
boolean replacing = false;
+ boolean keyguardGoingAwayWithWallpaper = false;
+
for (int i = windows.size() - 1; i >= 0; i--) {
w = windows.get(i);
if ((w.mAttrs.type == TYPE_WALLPAPER)) {
@@ -506,13 +511,11 @@
inFreeformSpace = stack != null && stack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
}
- replacing = replacing || w.mWillReplaceWindow;
+ replacing |= w.mWillReplaceWindow;
+ keyguardGoingAwayWithWallpaper |= (w.mAppToken != null
+ && w.mWinAnimator.mKeyguardGoingAwayWithWallpaper);
- // If the app is executing an animation because the keyguard is going away (and the
- // keyguard was showing the wallpaper) keep the wallpaper during the animation so it
- // doesn't flicker out.
- final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
- || (w.mAppToken != null && w.mWinAnimator.mKeyguardGoingAwayWithWallpaper);
+ final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
if (hasWallpaper && w.isOnScreen() && (mWallpaperTarget == w || w.isDrawFinishedLw())) {
if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: #" + i + "=" + w);
result.setWallpaperTarget(w, i);
@@ -529,18 +532,26 @@
}
}
- if (result.wallpaperTarget == null && windowDetachedI >= 0) {
+ if (result.wallpaperTarget != null) {
+ return;
+ }
+
+ if (windowDetachedI >= 0) {
if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
"Found animating detached wallpaper activity: #" + windowDetachedI + "=" + w);
result.setWallpaperTarget(w, windowDetachedI);
- }
- if (result.wallpaperTarget == null
- && (inFreeformSpace || (replacing && mWallpaperTarget != null))) {
+ } else if (inFreeformSpace || (replacing && mWallpaperTarget != null)) {
// In freeform mode we set the wallpaper as its own target, so we don't need an
// additional window to make it visible. When we are replacing a window and there was
// wallpaper before replacement, we want to keep the window until the new windows fully
// appear and can determine the visibility, to avoid flickering.
result.setWallpaperTarget(result.topWallpaper, result.topWallpaperIndex);
+
+ } else if (keyguardGoingAwayWithWallpaper) {
+ // If the app is executing an animation because the keyguard is going away (and the
+ // keyguard was showing the wallpaper) keep the wallpaper during the animation so it
+ // doesn't flicker out by having it be its own target.
+ result.setWallpaperTarget(result.topWallpaper, result.topWallpaperIndex);
}
}
@@ -575,27 +586,24 @@
if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
"New i: " + wallpaperTargetIndex + " old i: " + oldI);
if (oldI >= 0) {
- if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
- "Animating wallpapers: old#" + oldI + "=" + oldW + "; new#"
- + wallpaperTargetIndex + "=" + wallpaperTarget);
+ final boolean newTargetHidden =
+ wallpaperTarget.mAppToken != null && wallpaperTarget.mAppToken.hiddenRequested;
+ final boolean oldTargetHidden =
+ oldW.mAppToken != null && oldW.mAppToken.hiddenRequested;
+ if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Animating wallpapers:"
+ + " old#" + oldI + "=" + oldW + " hidden=" + oldTargetHidden
+ + " new#" + wallpaperTargetIndex + "=" + wallpaperTarget
+ + " hidden=" + newTargetHidden);
- // Set the new target correctly.
- if (wallpaperTarget.mAppToken != null
- && wallpaperTarget.mAppToken.hiddenRequested) {
- if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
- "Old wallpaper still the target.");
- mWallpaperTarget = oldW;
- wallpaperTarget = oldW;
- wallpaperTargetIndex = oldI;
- }
- // Now set the upper and lower wallpaper targets correctly,
+ // Set the upper and lower wallpaper targets correctly,
// and make sure that we are positioning the wallpaper below the lower.
- else if (wallpaperTargetIndex > oldI) {
+ if (wallpaperTargetIndex > oldI) {
// The new target is on top of the old one.
if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
"Found target above old target.");
mUpperWallpaperTarget = wallpaperTarget;
mLowerWallpaperTarget = oldW;
+
wallpaperTarget = oldW;
wallpaperTargetIndex = oldI;
} else {
@@ -605,6 +613,22 @@
mUpperWallpaperTarget = oldW;
mLowerWallpaperTarget = wallpaperTarget;
}
+ if (newTargetHidden && !oldTargetHidden) {
+ if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
+ "Old wallpaper still the target.");
+ // Use the old target if new target is hidden but old target
+ // is not. If they're both hidden, still use the new target.
+ mWallpaperTarget = oldW;
+ } else if (newTargetHidden == oldTargetHidden
+ && !mService.mOpeningApps.contains(wallpaperTarget.mAppToken)
+ && (mService.mOpeningApps.contains(oldW.mAppToken)
+ || mService.mClosingApps.contains(oldW.mAppToken))) {
+ // If they're both hidden (or both not hidden), prefer the one that's
+ // currently in opening or closing app list, this allows transition
+ // selection logic to better determine the wallpaper status of
+ // opening/closing apps.
+ mWallpaperTarget = oldW;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index be060d2..b0d357c 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
@@ -118,6 +119,8 @@
// check if some got replaced and can be removed.
private boolean mRemoveReplacedWindows = false;
+ private final AppTokenList mTmpExitingAppTokens = new AppTokenList();
+
private String forceHidingToString() {
switch (mForceHiding) {
case KEYGUARD_NOT_SHOWN: return "KEYGUARD_NOT_SHOWN";
@@ -188,10 +191,19 @@
}
}
- final AppTokenList exitingAppTokens = stack.mExitingAppTokens;
- final int exitingCount = exitingAppTokens.size();
+ mTmpExitingAppTokens.clear();
+ mTmpExitingAppTokens.addAll(stack.mExitingAppTokens);
+
+ final int exitingCount = mTmpExitingAppTokens.size();
for (int i = 0; i < exitingCount; i++) {
- final AppWindowAnimator appAnimator = exitingAppTokens.get(i).mAppAnimator;
+ final AppWindowAnimator appAnimator = mTmpExitingAppTokens.get(i).mAppAnimator;
+ // stepAnimation can trigger finishExit->removeWindowInnerLocked
+ // ->performSurfacePlacement
+ // performSurfacePlacement will directly manipulate the mExitingAppTokens list
+ // so we need to iterate over a copy and check for modifications.
+ if (!stack.mExitingAppTokens.contains(appAnimator)) {
+ continue;
+ }
appAnimator.wasAnimating = appAnimator.animating;
if (appAnimator.stepAnimationLocked(mCurrentTime, displayId)) {
setAnimating(true);
@@ -233,6 +245,12 @@
|| (win.mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) != 0;
}
+ // Allow showing a window that dismisses Keyguard if the policy allows it. This is used for
+ // when the policy knows that the Keyguard can be dismissed without user interaction to
+ // provide a smooth transition in that case.
+ allowWhenLocked |= (win.mAttrs.flags & FLAG_DISMISS_KEYGUARD) != 0
+ && mPolicy.canShowDismissingWindowWhileLockedLw();
+
// Only hide windows if the keyguard is active and not animating away.
boolean keyguardOn = mPolicy.isKeyguardShowingOrOccluded()
&& mForceHiding != KEYGUARD_ANIMATING_OUT;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3687512..eb9ad6c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -41,8 +41,10 @@
import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
+import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.Region;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
@@ -189,6 +191,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
@@ -291,6 +294,9 @@
/** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */
static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000;
+ /** Amount of time (in milliseconds) to delay before declaring a seamless rotation timeout. */
+ static final int SEAMLESS_ROTATION_TIMEOUT_DURATION = 2000;
+
/** Amount of time (in milliseconds) to delay before declaring a window replacement timeout. */
static final int WINDOW_REPLACEMENT_TIMEOUT_DURATION = 2000;
@@ -516,6 +522,9 @@
final Rect mTmpRect = new Rect();
final Rect mTmpRect2 = new Rect();
final Rect mTmpRect3 = new Rect();
+ final RectF mTmpRectF = new RectF();
+
+ final Matrix mTmpTransform = new Matrix();
boolean mDisplayReady;
boolean mSafeMode;
@@ -537,7 +546,7 @@
SparseArray<DisplayContent> mDisplayContents = new SparseArray<>(2);
int mRotation = 0;
- int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ int mLastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
boolean mAltOrientation = false;
private boolean mKeyguardWaitingForActivityDrawn;
@@ -2899,9 +2908,23 @@
}
result |= RELAYOUT_RES_SURFACE_CHANGED;
}
+ final WindowSurfaceController surfaceController = winAnimator.mSurfaceController;
+ if (viewVisibility == View.VISIBLE && surfaceController != null) {
+ // We already told the client to go invisible, but the message may not be
+ // handled yet, or it might want to draw a last frame. If we already have a
+ // surface, let the client use that, but don't create new surface at this point.
+ surfaceController.getSurface(outSurface);
+ } else {
+ if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win);
- outSurface.release();
- if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win);
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmReleaseOutSurface_"
+ + win.mAttrs.getTitle());
+ outSurface.release();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+ }
}
if (focusMayChange) {
@@ -3036,6 +3059,7 @@
} else {
// For some reason there isn't a surface. Clear the
// caller's object so they see the same state.
+ Slog.w(TAG_WM, "Failed to create surface control for " + win);
outSurface.release();
}
return result;
@@ -3058,6 +3082,9 @@
if (oldVisibility == View.GONE) {
winAnimator.mEnterAnimationPending = true;
}
+
+ win.mLastVisibleLayoutRotation = mRotation;
+
winAnimator.mEnteringAnimation = true;
if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
win.prepareWindowToDisplayDuringRelayout(outConfig);
@@ -3161,6 +3188,7 @@
// frozen, there is no reason to animate and it can cause strange
// artifacts when we unfreeze the display if some different animation
// is running.
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WM#applyAnimationLocked");
if (okToDisplay()) {
DisplayInfo displayInfo = getDefaultDisplayInfoLocked();
final int width = displayInfo.appWidth;
@@ -3212,6 +3240,7 @@
} else {
atoken.mAppAnimator.clearAnimation();
}
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
return atoken.mAppAnimator.animation != null;
}
@@ -3420,7 +3449,7 @@
int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int userId,
int configChanges, boolean voiceInteraction, boolean launchTaskBehind,
Rect taskBounds, Configuration config, int taskResizeMode, boolean alwaysFocusable,
- boolean homeTask, int targetSdkVersion) {
+ boolean homeTask, int targetSdkVersion, int rotationAnimationHint) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"addAppToken()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -3458,6 +3487,7 @@
atoken.mAlwaysFocusable = alwaysFocusable;
if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken
+ " to stack=" + stackId + " task=" + taskId + " at " + addPos);
+ atoken.mRotationAnimationHint = rotationAnimationHint;
Task task = mTaskIdToTask.get(taskId);
if (task == null) {
@@ -3510,6 +3540,16 @@
// can re-appear and inflict its own orientation on us. Keep the
// orientation stable until this all settles down.
return mLastWindowForcedOrientation;
+ } else if (mPolicy.isKeyguardLocked()) {
+ // Use the last orientation the while the display is frozen with the
+ // keyguard locked. This could be the keyguard forced orientation or
+ // from a SHOW_WHEN_LOCKED window. We don't want to check the show when
+ // locked window directly though as things aren't stable while
+ // the display is frozen, for example the window could be momentarily unavailable
+ // due to activity relaunch.
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display is frozen while keyguard locked, "
+ + "return " + mLastOrientation);
+ return mLastOrientation;
}
} else {
// TODO(multidisplay): Change to the correct display.
@@ -3639,12 +3679,12 @@
}
}
if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
- "No app is requesting an orientation, return " + mForcedAppOrientation);
+ "No app is requesting an orientation, return " + mLastOrientation);
// The next app has not been requested to be visible, so we keep the current orientation
// to prevent freezing/unfreezing the display too early unless we are in multi-window, in
// which we don't let the app customize the orientation unless it was the home task that
// is handled above.
- return inMultiWindow ? SCREEN_ORIENTATION_UNSPECIFIED : mForcedAppOrientation;
+ return inMultiWindow ? SCREEN_ORIENTATION_UNSPECIFIED : mLastOrientation;
}
@Override
@@ -3727,8 +3767,8 @@
long ident = Binder.clearCallingIdentity();
try {
int req = getOrientationLocked();
- if (req != mForcedAppOrientation) {
- mForcedAppOrientation = req;
+ if (req != mLastOrientation) {
+ mLastOrientation = req;
//send a message to Policy indicating orientation change to take
//action like disabling/enabling sensors etc.,
mPolicy.setCurrentOrientationLw(req);
@@ -4440,7 +4480,25 @@
}
@Override
- public void notifyAppStopped(IBinder token, boolean stopped) {
+ public void notifyAppResumed(IBinder token, boolean wasStopped, boolean allowSavedSurface) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "notifyAppResumed()")) {
+ throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
+ }
+
+ synchronized(mWindowMap) {
+ final AppWindowToken wtoken;
+ wtoken = findAppWindowToken(token);
+ if (wtoken == null) {
+ Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: " + token);
+ return;
+ }
+ wtoken.notifyAppResumed(wasStopped, allowSavedSurface);
+ }
+ }
+
+ @Override
+ public void notifyAppStopped(IBinder token) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"notifyAppStopped()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -4450,10 +4508,10 @@
final AppWindowToken wtoken;
wtoken = findAppWindowToken(token);
if (wtoken == null) {
- Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " + token);
+ Slog.w(TAG_WM, "Attempted to notify stopped of non-existing app token: " + token);
return;
}
- wtoken.notifyAppStopped(stopped);
+ wtoken.notifyAppStopped();
}
}
@@ -4990,18 +5048,32 @@
try {
synchronized (mWindowMap) {
final DisplayContent displayContent = mDisplayContents.get(displayId);
+ boolean attachedToDisplay = false;
if (displayContent != null) {
TaskStack stack = mStackIdToStack.get(stackId);
if (stack == null) {
if (DEBUG_STACK) Slog.d(TAG_WM, "attachStack: stackId=" + stackId);
- stack = new TaskStack(this, stackId);
+
+ stack = displayContent.getStackById(stackId);
+ if (stack != null) {
+ // It's already attached to the display. Detach and re-attach
+ // because onTop might change, and be sure to clear mDeferDetach!
+ displayContent.detachStack(stack);
+ stack.mDeferDetach = false;
+ attachedToDisplay = true;
+ } else {
+ stack = new TaskStack(this, stackId);
+ }
+
mStackIdToStack.put(stackId, stack);
if (stackId == DOCKED_STACK_ID) {
getDefaultDisplayContentLocked().mDividerControllerLocked
.notifyDockedStackExistsChanged(true);
}
}
- stack.attachDisplayContent(displayContent);
+ if (!attachedToDisplay) {
+ stack.attachDisplayContent(displayContent);
+ }
displayContent.attachStack(stack, onTop);
if (stack.getRawFullscreen()) {
return null;
@@ -5652,6 +5724,12 @@
// Called by window manager policy. Not exposed externally.
@Override
+ public void reboot(boolean confirm) {
+ ShutdownThread.reboot(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, confirm);
+ }
+
+ // Called by window manager policy. Not exposed externally.
+ @Override
public void rebootSafeMode(boolean confirm) {
ShutdownThread.rebootSafeMode(mContext, confirm);
}
@@ -5809,7 +5887,8 @@
if (w.isDrawnLw()) {
if (w.mAttrs.type == TYPE_BOOT_PROGRESS) {
haveBootMsg = true;
- } else if (w.mAttrs.type == TYPE_APPLICATION) {
+ } else if (w.mAttrs.type == TYPE_APPLICATION
+ || w.mAttrs.type == TYPE_DRAWN_APPLICATION) {
haveApp = true;
} else if (w.mAttrs.type == TYPE_WALLPAPER) {
haveWallpaper = true;
@@ -6141,6 +6220,21 @@
}
}
+ @Override
+ public Bitmap screenshotWallpaper() {
+ if (!checkCallingPermission(Manifest.permission.READ_FRAME_BUFFER,
+ "screenshotWallpaper()")) {
+ throw new SecurityException("Requires READ_FRAME_BUFFER permission");
+ }
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper");
+ return screenshotApplicationsInner(null, Display.DEFAULT_DISPLAY, -1, -1, true, 1f,
+ Bitmap.Config.ARGB_8888, true);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+ }
+
/**
* Takes a snapshot of the screen. In landscape mode this grabs the whole screen.
* In portrait mode, it grabs the upper region of the screen based on the vertical dimension
@@ -6157,7 +6251,7 @@
@Override
public void run() {
Bitmap bm = screenshotApplicationsInner(null, Display.DEFAULT_DISPLAY, -1, -1,
- true, 1f, Bitmap.Config.ARGB_8888);
+ true, 1f, Bitmap.Config.ARGB_8888, false);
try {
receiver.send(bm);
} catch (RemoteException e) {
@@ -6187,14 +6281,27 @@
try {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenshotApplications");
return screenshotApplicationsInner(appToken, displayId, width, height, false,
- frameScale, Bitmap.Config.RGB_565);
+ frameScale, Bitmap.Config.RGB_565, false);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
}
+ /**
+ * Takes a snapshot of the screen. In landscape mode this grabs the whole screen.
+ * In portrait mode, it grabs the full screenshot.
+ *
+ * @param displayId the Display to take a screenshot of.
+ * @param width the width of the target bitmap
+ * @param height the height of the target bitmap
+ * @param includeFullDisplay true if the screen should not be cropped before capture
+ * @param frameScale the scale to apply to the frame, only used when width = -1 and height = -1
+ * @param config of the output bitmap
+ * @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot
+ */
Bitmap screenshotApplicationsInner(IBinder appToken, int displayId, int width, int height,
- boolean includeFullDisplay, float frameScale, Bitmap.Config config) {
+ boolean includeFullDisplay, float frameScale, Bitmap.Config config,
+ boolean wallpaperOnly) {
final DisplayContent displayContent;
synchronized(mWindowMap) {
displayContent = getDisplayContentLocked(displayId);
@@ -6221,7 +6328,7 @@
boolean screenshotReady;
int minLayer;
- if (appToken == null) {
+ if (appToken == null && !wallpaperOnly) {
screenshotReady = true;
minLayer = 0;
} else {
@@ -6261,11 +6368,20 @@
if (ws.mLayer >= aboveAppLayer) {
continue;
}
+ if (wallpaperOnly && !ws.mIsWallpaper) {
+ continue;
+ }
if (ws.mIsImWindow) {
if (!includeImeInScreenshot) {
continue;
}
} else if (ws.mIsWallpaper) {
+ // If this is the wallpaper layer and we're only looking for the wallpaper layer
+ // then the target window state is this one.
+ if (wallpaperOnly) {
+ appWin = ws;
+ }
+
if (appWin == null) {
// We have not ran across the target window yet, so it is probably
// behind the wallpaper. This can happen when the keyguard is up and
@@ -6313,8 +6429,10 @@
}
}
- if (ws.mAppToken != null && ws.mAppToken.token == appToken &&
- ws.isDisplayedLw() && winAnim.getShown()) {
+ final boolean foundTargetWs =
+ (ws.mAppToken != null && ws.mAppToken.token == appToken)
+ || (appWin != null && wallpaperOnly);
+ if (foundTargetWs && ws.isDisplayedLw() && winAnim.getShown()) {
screenshotReady = true;
}
@@ -6567,6 +6685,7 @@
Binder.restoreCallingIdentity(origId);
}
+
// TODO(multidisplay): Rotate any display?
/**
* Updates the current rotation.
@@ -6603,13 +6722,13 @@
// an orientation that has different metrics than it expected.
// eg. Portrait instead of Landscape.
- int rotation = mPolicy.rotationForOrientationLw(mForcedAppOrientation, mRotation);
+ int rotation = mPolicy.rotationForOrientationLw(mLastOrientation, mRotation);
boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw(
- mForcedAppOrientation, rotation);
+ mLastOrientation, rotation);
if (DEBUG_ORIENTATION) {
- Slog.v(TAG_WM, "Application requested orientation "
- + mForcedAppOrientation + ", got rotation " + rotation
+ Slog.v(TAG_WM, "Selected orientation "
+ + mLastOrientation + ", got rotation " + rotation
+ " which has " + (altOrientation ? "incompatible" : "compatible")
+ " metrics");
}
@@ -6623,9 +6742,11 @@
Slog.v(TAG_WM,
"Rotation changed to " + rotation + (altOrientation ? " (alt)" : "")
+ " from " + mRotation + (mAltOrientation ? " (alt)" : "")
- + ", forceApp=" + mForcedAppOrientation);
+ + ", lastOrientation=" + mLastOrientation);
}
+ int oldRotation = mRotation;
+
mRotation = rotation;
mAltOrientation = altOrientation;
mPolicy.setRotationLw(mRotation);
@@ -6642,10 +6763,46 @@
} else {
mPolicy.selectRotationAnimationLw(anim);
}
- startFreezingDisplayLocked(inTransaction, anim[0], anim[1]);
- // startFreezingDisplayLocked can reset the ScreenRotationAnimation.
- screenRotationAnimation =
+ boolean rotateSeamlessly = mPolicy.shouldRotateSeamlessly(oldRotation, mRotation);
+ final WindowList windows = displayContent.getWindowList();
+ // We can't rotate seamlessly while an existing seamless rotation is still
+ // waiting on windows to finish drawing.
+ if (rotateSeamlessly) {
+ for (int i = windows.size() - 1; i >= 0; i--) {
+ WindowState w = windows.get(i);
+ if (w.mSeamlesslyRotated) {
+ rotateSeamlessly = false;
+ break;
+ }
+ // In what can only be called an unfortunate workaround we require
+ // seamlessly rotated child windows to have the TRANSFORM_TO_DISPLAY_INVERSE
+ // flag. Due to limitations in the client API, there is no way for
+ // the client to set this flag in a race free fashion. If we seamlessly rotate
+ // a window which does not have this flag, but then gains it, we will get
+ // an incorrect visual result (rotated viewfinder). This means if we want to
+ // support seamlessly rotating windows which could gain this flag, we can't
+ // rotate windows without it. This limits seamless rotation in N to camera framework
+ // users, windows without children, and native code. This is unfortunate but
+ // having the camera work is our primary goal.
+ if (w.isChildWindow() & w.isVisibleNow() &&
+ !w.mWinAnimator.mSurfaceController.getTransformToDisplayInverse()) {
+ rotateSeamlessly = false;
+ }
+ }
+ }
+
+ if (!rotateSeamlessly) {
+ startFreezingDisplayLocked(inTransaction, anim[0], anim[1]);
+ // startFreezingDisplayLocked can reset the ScreenRotationAnimation.
+ screenRotationAnimation =
mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
+ } else {
+ // The screen rotation animation uses a screenshot to freeze the screen
+ // while windows resize underneath.
+ // When we are rotating seamlessly, we allow the elements to transition
+ // to their rotated state independently and without a freeze required.
+ screenRotationAnimation = null;
+ }
// We need to update our screen size information to match the new rotation. If the rotation
// has actually changed then this method will return true and, according to the comment at
@@ -6674,6 +6831,13 @@
}
}
+ if (rotateSeamlessly) {
+ for (int i = windows.size() - 1; i >= 0; i--) {
+ WindowState w = windows.get(i);
+ w.mWinAnimator.seamlesslyRotateWindow(oldRotation, mRotation);
+ }
+ }
+
mDisplayManagerInternal.performTraversalInTransactionFromWindowManager();
} finally {
if (!inTransaction) {
@@ -6684,19 +6848,22 @@
}
}
- final WindowList windows = displayContent.getWindowList();
for (int i = windows.size() - 1; i >= 0; i--) {
WindowState w = windows.get(i);
// Discard surface after orientation change, these can't be reused.
if (w.mAppToken != null) {
w.mAppToken.destroySavedSurfaces();
}
- if (w.mHasSurface) {
+ if (w.mHasSurface && !rotateSeamlessly) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Set mOrientationChanging of " + w);
w.mOrientationChanging = true;
mWindowPlacerLocked.mOrientationChangeComplete = false;
+ w.mLastFreezeDuration = 0;
}
- w.mLastFreezeDuration = 0;
+ }
+ if (rotateSeamlessly) {
+ mH.removeMessages(H.SEAMLESS_ROTATION_TIMEOUT);
+ mH.sendEmptyMessageDelayed(H.SEAMLESS_ROTATION_TIMEOUT, SEAMLESS_ROTATION_TIMEOUT_DURATION);
}
for (int i=mRotationWatchers.size()-1; i>=0; i--) {
@@ -6706,7 +6873,7 @@
}
}
- //TODO (multidisplay): Magnification is supported only for the default display.
+ // TODO (multidisplay): Magnification is supported only for the default display.
// Announce rotation only if we will not animate as we already have the
// windows in final state. Otherwise, we make this call at the rotation end.
if (screenRotationAnimation == null && mAccessibilityController != null
@@ -7994,8 +8161,8 @@
public static final int NOTIFY_STARTING_WINDOW_DRAWN = 50;
public static final int UPDATE_ANIMATION_SCALE = 51;
public static final int WINDOW_REMOVE_TIMEOUT = 52;
-
public static final int NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED = 53;
+ public static final int SEAMLESS_ROTATION_TIMEOUT = 54;
/**
* Used to denote that an integer field in a message will not be used.
@@ -8635,6 +8802,27 @@
mAmInternal.notifyDockedStackMinimizedChanged(msg.arg1 == 1);
}
break;
+ case SEAMLESS_ROTATION_TIMEOUT: {
+ // Rotation only supported on primary display.
+ // TODO(multi-display)
+ synchronized(mWindowMap) {
+ final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ final WindowList windows = displayContent.getWindowList();
+ boolean layoutNeeded = false;
+ for (int i = windows.size() - 1; i >= 0; i--) {
+ WindowState w = windows.get(i);
+ if (w.mSeamlesslyRotated) {
+ layoutNeeded = true;
+ w.setDisplayLayoutNeeded();
+ }
+ w.mSeamlesslyRotated = false;
+ }
+ if (layoutNeeded) {
+ mWindowPlacerLocked.performSurfacePlacement();
+ }
+ }
+ }
+ break;
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG_WM, "handleMessage: exit");
@@ -9294,7 +9482,7 @@
if (task != null && task.mStack.getBoundsAnimating()) {
return;
}
- w.setInsetsChanged();
+ w.setReportResizeHints();
boolean configChanged = w.isConfigChanged();
if (DEBUG_CONFIGURATION && configChanged) {
Slog.v(TAG_WM, "Win " + w + " config changed: "
@@ -9302,15 +9490,22 @@
}
final boolean dragResizingChanged = w.isDragResizeChanged()
&& !w.isDragResizingChangeReported();
+
if (localLOGV) Slog.v(TAG_WM, "Resizing " + w
+ ": configChanged=" + configChanged
+ " dragResizingChanged=" + dragResizingChanged
+ " last=" + w.mLastFrame + " frame=" + w.mFrame);
+
+ // We update mLastFrame always rather than in the conditional with the
+ // last inset variables, because mFrameSizeChanged only tracks the
+ // width and height changing.
w.mLastFrame.set(w.mFrame);
+
if (w.mContentInsetsChanged
|| w.mVisibleInsetsChanged
|| winAnimator.mSurfaceResized
|| w.mOutsetsChanged
+ || w.mFrameSizeChanged
|| configChanged
|| dragResizingChanged
|| !w.isResizedWhileNotDragResizingReported()) {
@@ -10143,6 +10338,15 @@
}
}
+ public void notifyAppRelaunchesCleared(IBinder token) {
+ synchronized (mWindowMap) {
+ final AppWindowToken appWindow = findAppWindowToken(token);
+ if (appWindow != null) {
+ appWindow.clearRelaunching();
+ }
+ }
+ }
+
@Override
public int getDockedDividerInsetsLw() {
return getDefaultDisplayContentLocked().getDockedDividerController().getContentInsets();
@@ -10404,7 +10608,7 @@
pw.print(" mRotation="); pw.print(mRotation);
pw.print(" mAltOrientation="); pw.println(mAltOrientation);
pw.print(" mLastWindowForcedOrientation="); pw.print(mLastWindowForcedOrientation);
- pw.print(" mForcedAppOrientation="); pw.println(mForcedAppOrientation);
+ pw.print(" mLastOrientation="); pw.println(mLastOrientation);
pw.print(" mDeferredRotationPauseCount="); pw.println(mDeferredRotationPauseCount);
pw.print(" Animation settings: disabled="); pw.print(mAnimationsDisabled);
pw.print(" window="); pw.print(mWindowAnimationScaleSetting);
@@ -11283,8 +11487,9 @@
for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
final WindowState win = windows.get(winNdx);
final boolean isForceHiding = mPolicy.isForceHiding(win.mAttrs);
+ final boolean keyguard = mPolicy.isKeyguardHostWindow(win.mAttrs);
if (win.isVisibleLw()
- && (win.mAppToken != null || isForceHiding)) {
+ && (win.mAppToken != null || isForceHiding || keyguard)) {
win.mWinAnimator.mDrawState = DRAW_PENDING;
// Force add to mResizingWindows.
win.mLastContentInsets.set(-1, -1, -1, -1);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 08bfa2d..54f60ef 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -87,6 +87,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
@@ -287,6 +288,7 @@
// "Real" frame that the application sees, in display coordinate space.
final Rect mFrame = new Rect();
final Rect mLastFrame = new Rect();
+ boolean mFrameSizeChanged = false;
// Frame that is scaled to the application's coordinate space when in
// screen size compatibility mode.
final Rect mCompatFrame = new Rect();
@@ -394,6 +396,13 @@
boolean mOrientationChanging;
/**
+ * The orientation during the last visible call to relayout. If our
+ * current orientation is different, the window can't be ready
+ * to be shown.
+ */
+ int mLastVisibleLayoutRotation = -1;
+
+ /**
* How long we last kept the screen frozen.
*/
int mLastFreezeDuration;
@@ -494,6 +503,15 @@
/** @see #isResizedWhileNotDragResizingReported(). */
private boolean mResizedWhileNotDragResizingReported;
+ /**
+ * During seamless rotation we have two phases, first the old window contents
+ * are rotated to look as if they didn't move in the new coordinate system. Then we
+ * have to freeze updates to this layer (to preserve the transformation) until
+ * the resize actually occurs. This is true from when the transformation is set
+ * and false until the transaction to resize is sent.
+ */
+ boolean mSeamlesslyRotated = false;
+
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a,
int viewVisibility, final DisplayContent displayContent) {
@@ -812,10 +830,10 @@
final int height = Math.min(mFrame.height(), mContentFrame.height());
final int width = Math.min(mContentFrame.width(), mFrame.width());
final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
- final int minVisibleHeight = WindowManagerService.dipToPixel(
- MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics);
- final int minVisibleWidth = WindowManagerService.dipToPixel(
- MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics);
+ final int minVisibleHeight = Math.min(height, WindowManagerService.dipToPixel(
+ MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics));
+ final int minVisibleWidth = Math.min(width, WindowManagerService.dipToPixel(
+ MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics));
final int top = Math.max(mContentFrame.top,
Math.min(mFrame.top, mContentFrame.bottom - minVisibleHeight));
final int left = Math.max(mContentFrame.left + minVisibleWidth - width,
@@ -1046,14 +1064,16 @@
return mAppToken != null && mAppToken.voiceInteraction;
}
- boolean setInsetsChanged() {
+ boolean setReportResizeHints() {
mOverscanInsetsChanged |= !mLastOverscanInsets.equals(mOverscanInsets);
mContentInsetsChanged |= !mLastContentInsets.equals(mContentInsets);
mVisibleInsetsChanged |= !mLastVisibleInsets.equals(mVisibleInsets);
mStableInsetsChanged |= !mLastStableInsets.equals(mStableInsets);
mOutsetsChanged |= !mLastOutsets.equals(mOutsets);
+ mFrameSizeChanged |= (mLastFrame.width() != mFrame.width()) ||
+ (mLastFrame.height() != mFrame.height());
return mOverscanInsetsChanged || mContentInsetsChanged || mVisibleInsetsChanged
- || mOutsetsChanged;
+ || mOutsetsChanged || mFrameSizeChanged;
}
public DisplayContent getDisplayContent() {
@@ -1270,7 +1290,8 @@
final boolean isViewVisible = (mAppToken == null || !mAppToken.clientHidden)
&& (mViewVisibility == View.VISIBLE) && !mWindowRemovalAllowed;
return (isOnScreenIgnoringKeyguard() && (!visibleOnly || isViewVisible)
- || mWinAnimator.mAttrType == TYPE_BASE_APPLICATION)
+ || mWinAnimator.mAttrType == TYPE_BASE_APPLICATION
+ || mWinAnimator.mAttrType == TYPE_DRAWN_APPLICATION)
&& !mAnimatingExit && !mDestroying;
}
@@ -1315,7 +1336,8 @@
&& ((!mAttachedHidden && mViewVisibility == View.VISIBLE && !mRootToken.hidden)
|| mWinAnimator.mAnimation != null
|| ((atoken != null) && (atoken.mAppAnimator.animation != null)
- && !mWinAnimator.isDummyAnimation()));
+ && !mWinAnimator.isDummyAnimation())
+ || isAnimatingWithSavedSurface());
}
/**
@@ -2083,6 +2105,7 @@
Slog.v(TAG, "Destroying saved surface: " + this);
}
mWinAnimator.destroySurfaceLocked();
+ mSurfaceSaved = false;
}
mWasVisibleBeforeClientHidden = false;
}
@@ -2091,7 +2114,18 @@
if (!mSurfaceSaved) {
return;
}
+
+ // Sometimes we save surfaces due to layout invisible
+ // directly after rotation occurs. However this means
+ // the surface was never laid out in the new orientation.
+ // We can only restore to the last rotation we were
+ // laid out as visible in.
+ if (mLastVisibleLayoutRotation != mService.mRotation) {
+ destroySavedSurface();
+ return;
+ }
mSurfaceSaved = false;
+
if (mWinAnimator.mSurfaceController != null) {
setHasSurface(true);
mWinAnimator.mDrawState = WindowStateAnimator.READY_TO_SHOW;
@@ -2335,6 +2369,7 @@
mVisibleInsetsChanged = false;
mStableInsetsChanged = false;
mOutsetsChanged = false;
+ mFrameSizeChanged = false;
mResizedWhileNotDragResizingReported = true;
mWinAnimator.mSurfaceResized = false;
} catch (RemoteException e) {
@@ -2892,11 +2927,24 @@
// for only child windows (as the main window is handled by window preservation)
// and the big surface.
//
- // Though windows of TYPE_APPLICATION (as opposed to TYPE_BASE_APPLICATION)
- // are not children in the sense of an attached window, we also want to replace
- // them at such phases, as they won't be covered by window preservation,
- // and in general we expect them to return following relaunch.
+ // Though windows of TYPE_APPLICATION or TYPE_DRAWN_APPLICATION (as opposed to
+ // TYPE_BASE_APPLICATION) are not children in the sense of an attached window,
+ // we also want to replace them at such phases, as they won't be covered by window
+ // preservation, and in general we expect them to return following relaunch.
boolean shouldBeReplacedWithChildren() {
- return isChildWindow() || mAttrs.type == TYPE_APPLICATION;
+ return isChildWindow() || mAttrs.type == TYPE_APPLICATION
+ || mAttrs.type == TYPE_DRAWN_APPLICATION;
+ }
+
+ public int getRotationAnimationHint() {
+ if (mAppToken != null) {
+ return mAppToken.mRotationAnimationHint;
+ } else {
+ return -1;
+ }
+ }
+
+ public boolean isRtl() {
+ return mMergedConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 36d9697..00f4a45 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -51,12 +51,15 @@
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.Region;
import android.os.Debug;
import android.os.RemoteException;
+import android.os.Trace;
import android.util.Slog;
import android.view.DisplayInfo;
import android.view.MagnificationSpec;
+import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -923,6 +926,22 @@
mPendingDestroySurface = null;
}
+ void applyMagnificationSpec(MagnificationSpec spec, Matrix transform) {
+ final int surfaceInsetLeft = mWin.mAttrs.surfaceInsets.left;
+ final int surfaceInsetTop = mWin.mAttrs.surfaceInsets.top;
+
+ if (spec != null && !spec.isNop()) {
+ float scale = spec.scale;
+ transform.postScale(scale, scale);
+ transform.postTranslate(spec.offsetX, spec.offsetY);
+
+ // As we are scaling the whole surface, to keep the content
+ // in the same position we will also have to scale the surfaceInsets.
+ transform.postTranslate(-(surfaceInsetLeft*scale - surfaceInsetLeft),
+ -(surfaceInsetTop*scale - surfaceInsetTop));
+ }
+ }
+
void computeShownFrameLocked() {
final boolean selfTransformation = mHasLocalTransformation;
Transformation attachedTransformation =
@@ -1013,10 +1032,7 @@
if (mService.mAccessibilityController != null && displayId == DEFAULT_DISPLAY) {
MagnificationSpec spec = mService.mAccessibilityController
.getMagnificationSpecForWindowLocked(mWin);
- if (spec != null && !spec.isNop()) {
- tmpMatrix.postScale(spec.scale, spec.scale);
- tmpMatrix.postTranslate(spec.offsetX, spec.offsetY);
- }
+ applyMagnificationSpec(spec, tmpMatrix);
}
// "convert" it into SurfaceFlinger's format
@@ -1115,10 +1131,7 @@
tmpMatrix.setScale(mWin.mGlobalScale, mWin.mGlobalScale);
tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
- if (spec != null && !spec.isNop()) {
- tmpMatrix.postScale(spec.scale, spec.scale);
- tmpMatrix.postTranslate(spec.offsetX, spec.offsetY);
- }
+ applyMagnificationSpec(spec, tmpMatrix);
tmpMatrix.getValues(tmpFloats);
@@ -1400,6 +1413,9 @@
mExtraHScale = (float) 1.0;
mExtraVScale = (float) 1.0;
+ boolean wasForceScaled = mForceScaleUntilResize;
+ boolean wasSeamlesslyRotated = w.mSeamlesslyRotated;
+
// Once relayout has been called at least once, we need to make sure
// we only resize the client surface during calls to relayout. For
// clients which use indeterminate measure specs (MATCH_PARENT),
@@ -1407,21 +1423,17 @@
// However, this would be unsafe, as the client may be in the middle
// of producing a frame at the old size, having just completed layout
// to find the surface size changed underneath it.
- //
- // TODO: For N we only apply this fix to the pinned workspace. As we
- // aren't observing known issues here outside of PiP resizing. (Typically
- // the other windows that use -1 are PopupWindows which aren't likely
- // to be rendering while we resize).
-
- boolean wasForceScaled = mForceScaleUntilResize;
-
- if (!w.inPinnedWorkspace() || (!w.mRelayoutCalled || w.mInRelayout)) {
+ if (!w.mRelayoutCalled || w.mInRelayout) {
mSurfaceResized = mSurfaceController.setSizeInTransaction(
mTmpSize.width(), mTmpSize.height(), recoveringMemory);
} else {
mSurfaceResized = false;
}
mForceScaleUntilResize = mForceScaleUntilResize && !mSurfaceResized;
+ // If we are undergoing seamless rotation, the surface has already
+ // been set up to persist at it's old location. We need to freeze
+ // updates until a resize occurs.
+ w.mSeamlesslyRotated = w.mSeamlesslyRotated && !mSurfaceResized;
calculateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect);
@@ -1471,17 +1483,20 @@
// will be seamless.
mForceScaleUntilResize = true;
} else {
- mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top,
- recoveringMemory);
+ if (!w.mSeamlesslyRotated) {
+ mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top,
+ recoveringMemory);
+ }
}
// If we are ending the scaling mode. We switch to SCALING_MODE_FREEZE
- // to prevent further updates until buffer latch. Normally position
- // would continue to apply immediately. But we need a different position
- // before and after resize (since we have scaled the shadows, as discussed
- // above).
- if (wasForceScaled && !mForceScaleUntilResize) {
- mSurfaceController.setPositionAppliesWithResizeInTransaction(true);
+ // to prevent further updates until buffer latch.
+ // When ending both force scaling, and seamless rotation, we need to freeze
+ // the Surface geometry until a buffer comes in at the new size (normally position and crop
+ // are unfrozen). setGeometryAppliesWithResizeInTransaction accomplishes this for us.
+ if ((wasForceScaled && !mForceScaleUntilResize) ||
+ (wasSeamlesslyRotated && !w.mSeamlesslyRotated)) {
+ mSurfaceController.setGeometryAppliesWithResizeInTransaction(true);
mSurfaceController.forceScaleableInTransaction(false);
}
@@ -1493,12 +1508,13 @@
-w.mAttrs.surfaceInsets.right, -w.mAttrs.surfaceInsets.bottom);
}
- updateSurfaceWindowCrop(clipRect, mTmpFinalClipRect, recoveringMemory);
-
- mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale * mExtraHScale,
- mDtDx * w.mVScale * mExtraVScale,
- mDsDy * w.mHScale * mExtraHScale,
- mDtDy * w.mVScale * mExtraVScale, recoveringMemory);
+ if (!w.mSeamlesslyRotated) {
+ updateSurfaceWindowCrop(clipRect, mTmpFinalClipRect, recoveringMemory);
+ mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale * mExtraHScale,
+ mDtDx * w.mVScale * mExtraVScale,
+ mDsDy * w.mHScale * mExtraHScale,
+ mDtDy * w.mVScale * mExtraVScale, recoveringMemory);
+ }
if (mSurfaceResized) {
mReportSurfaceResized = true;
@@ -1858,6 +1874,7 @@
// frozen, there is no reason to animate and it can cause strange
// artifacts when we unfreeze the display if some different animation
// is running.
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#applyAnimationLocked");
if (mService.okToDisplay()) {
int anim = mPolicy.selectAnimationLw(mWin, transit);
int attr = -1;
@@ -1897,6 +1914,8 @@
} else {
clearAnimation();
}
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+
if (mWin.mAttrs.type == TYPE_INPUT_METHOD) {
mService.adjustForImeIfNeeded(mWin.mDisplayContent);
if (isEntrance) {
@@ -2057,7 +2076,8 @@
if (time > mDeferTransactionTime + PENDING_TRANSACTION_FINISH_WAIT_TIME) {
mDeferTransactionTime = -1;
mDeferTransactionUntilFrame = -1;
- } else {
+ } else if (mWin.mAttachedWindow != null &&
+ mWin.mAttachedWindow.mWinAnimator.hasSurface()) {
mSurfaceController.deferTransactionUntil(
mWin.mAttachedWindow.mWinAnimator.mSurfaceController.getHandle(),
mDeferTransactionUntilFrame);
@@ -2084,4 +2104,98 @@
void endDelayingAnimationStart() {
mAnimationStartDelayed = false;
}
+
+ void seamlesslyRotateWindow(int oldRotation, int newRotation) {
+ final WindowState w = mWin;
+ if (!w.isVisibleNow() || w.mIsWallpaper) {
+ return;
+ }
+
+ final Rect cropRect = mService.mTmpRect;
+ final Rect displayRect = mService.mTmpRect2;
+ final RectF frameRect = mService.mTmpRectF;
+ final Matrix transform = mService.mTmpTransform;
+
+ final float x = w.mFrame.left;
+ final float y = w.mFrame.top;
+ final float width = w.mFrame.width();
+ final float height = w.mFrame.height();
+
+ mService.getDefaultDisplayContentLocked().getLogicalDisplayRect(displayRect);
+ final float displayWidth = displayRect.width();
+ final float displayHeight = displayRect.height();
+
+ // Compute a transform matrix to undo the coordinate space transformation,
+ // and present the window at the same physical position it previously occupied.
+ final int deltaRotation = DisplayContent.deltaRotation(newRotation, oldRotation);
+ switch (deltaRotation) {
+ case Surface.ROTATION_0:
+ transform.reset();
+ break;
+ case Surface.ROTATION_270:
+ transform.setRotate(270, 0, 0);
+ transform.postTranslate(0, displayHeight);
+ transform.postTranslate(y, 0);
+ break;
+ case Surface.ROTATION_180:
+ transform.reset();
+ break;
+ case Surface.ROTATION_90:
+ transform.setRotate(90, 0, 0);
+ transform.postTranslate(displayWidth, 0);
+ transform.postTranslate(-y, x);
+ break;
+ }
+
+ // We have two cases:
+ // 1. Windows with NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY:
+ // These windows never change buffer size when rotating. Rather the window manager
+ // just updates the scaling factors to fit in the new coordinate system,
+ // and SurfaceFlinger takes care of updating the buffer contents. So in this case
+ // we just need we just need to update the scaling factors and things are seamless
+ // already.
+ // 2. Other windows:
+ // In this case, we need to apply a rotation matrix to the window. For example
+ // if we have a portrait window and rotate to landscape, the window is still portrait
+ // and now extends off the bottom of the screen (and only halfway across). Essentially we
+ // apply a transform to display the current buffer at it's old position
+ // (in the new coordinate space). We then freeze layer updates until the resize
+ // occurs, at which point we undo, them.
+ if (w.isChildWindow() && mSurfaceController.getTransformToDisplayInverse()) {
+ frameRect.set(x, y, x+width, y+height);
+ transform.mapRect(frameRect);
+
+ w.mAttrs.x = (int) frameRect.left - w.mAttachedWindow.mFrame.left;
+ w.mAttrs.y = (int) frameRect.top - w.mAttachedWindow.mFrame.top;
+ w.mAttrs.width = (int) Math.ceil(frameRect.width());
+ w.mAttrs.height = (int) Math.ceil(frameRect.height());
+
+ w.setWindowScale(w.mRequestedWidth, w.mRequestedHeight);
+
+ w.applyGravityAndUpdateFrame(w.mContainingFrame, w.mDisplayFrame);
+ computeShownFrameLocked();
+ setSurfaceBoundariesLocked(false);
+
+ // The stack bounds will not yet be rotated at this point so setSurfaceBoundaries locked
+ // will crop us incorrectly. Overwrite the crop, exposing the full surface. By the next
+ // transaction this will be corrected.
+ cropRect.set(0, 0, w.mRequestedWidth, w.mRequestedWidth + w.mRequestedHeight);
+ mSurfaceController.setCropInTransaction(cropRect, false);
+ } else {
+ w.mSeamlesslyRotated = true;
+ transform.getValues(mService.mTmpFloats);
+
+ float DsDx = mService.mTmpFloats[Matrix.MSCALE_X];
+ float DtDx = mService.mTmpFloats[Matrix.MSKEW_Y];
+ float DsDy = mService.mTmpFloats[Matrix.MSKEW_X];
+ float DtDy = mService.mTmpFloats[Matrix.MSCALE_Y];
+ float nx = mService.mTmpFloats[Matrix.MTRANS_X];
+ float ny = mService.mTmpFloats[Matrix.MTRANS_Y];
+ mSurfaceController.setPositionInTransaction(nx, ny, false);
+ mSurfaceController.setMatrixInTransaction(DsDx * w.mHScale,
+ DtDx * w.mVScale,
+ DsDy * w.mHScale,
+ DtDy * w.mVScale, false);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 570a6ec..c77e572 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -260,8 +260,8 @@
}
}
- void setPositionAppliesWithResizeInTransaction(boolean recoveringMemory) {
- mSurfaceControl.setPositionAppliesWithResize();
+ void setGeometryAppliesWithResizeInTransaction(boolean recoveringMemory) {
+ mSurfaceControl.setGeometryAppliesWithResize();
}
void setMatrixInTransaction(float dsdx, float dtdx, float dsdy, float dtdy,
@@ -458,6 +458,10 @@
return mSurfaceControl.getHandle();
}
+ boolean getTransformToDisplayInverse() {
+ return mSurfaceControl.getTransformToDisplayInverse();
+ }
+
void getSurface(Surface outSurface) {
outSurface.copyFrom(mSurfaceControl);
}
@@ -582,10 +586,10 @@
}
@Override
- public void setPositionAppliesWithResize() {
- if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setPositionAppliesWithResize(): OLD: "
- + this + ". Called by" + Debug.getCallers(9));
- super.setPositionAppliesWithResize();
+ public void setGeometryAppliesWithResize() {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setGeometryAppliesWithResize(): OLD: "
+ + this + ". Called by" + Debug.getCallers(3));
+ super.setGeometryAppliesWithResize();
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 359063c..fa5e3ca 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -974,7 +974,7 @@
// windows, since that means "perform layout as normal,
// just don't display").
if (!gone || !win.mHaveFrame || win.mLayoutNeeded
- || ((win.isConfigChanged() || win.setInsetsChanged())
+ || ((win.isConfigChanged() || win.setReportResizeHints())
&& !win.isGoneForLayoutLw() &&
((win.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 ||
(win.mHasSurface && win.mAppToken != null &&
@@ -1074,6 +1074,8 @@
if (!transitionGoodToGo(appsCount)) {
return 0;
}
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
+
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
int transit = mService.mAppTransition.getAppTransition();
if (mService.mSkipAppTransitionAnimation) {
@@ -1095,6 +1097,26 @@
boolean fullscreenAnim = false;
boolean voiceInteraction = false;
+ int i;
+ for (i = 0; i < appsCount; i++) {
+ final AppWindowToken wtoken = mService.mOpeningApps.valueAt(i);
+ // Clearing the mAnimatingExit flag before entering animation. It's set to
+ // true if app window is removed, or window relayout to invisible.
+ // This also affects window visibility. We need to clear it *before*
+ // maybeUpdateTransitToWallpaper() as the transition selection depends on
+ // wallpaper target visibility.
+ wtoken.clearAnimatingFlags();
+
+ }
+ // Adjust wallpaper before we pull the lower/upper target, since pending changes
+ // (like the clearAnimatingFlags() above) might affect wallpaper target result.
+ final DisplayContent displayContent = mService.getDefaultDisplayContentLocked();
+ if ((displayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0 &&
+ mWallpaperControllerLocked.adjustWallpaperWindows()) {
+ mService.mLayersController.assignLayersLocked(windows);
+ displayContent.layoutNeeded = true;
+ }
+
final WindowState lowerWallpaperTarget =
mWallpaperControllerLocked.getLowerWallpaperTarget();
final WindowState upperWallpaperTarget =
@@ -1111,7 +1133,6 @@
upperWallpaperAppToken = upperWallpaperTarget.mAppToken;
}
- int i;
// Do a first pass through the tokens for two
// things:
// (1) Determine if both the closing and opening
@@ -1181,6 +1202,8 @@
final AppWindowToken topOpeningApp = handleOpeningApps(transit,
animLp, voiceInteraction, topClosingLayer);
+ mService.mAppTransition.setLastAppTransition(transit, topOpeningApp, topClosingApp);
+
final AppWindowAnimator openingAppAnimator = (topOpeningApp == null) ? null :
topOpeningApp.mAppAnimator;
final AppWindowAnimator closingAppAnimator = (topClosingApp == null) ? null :
@@ -1196,7 +1219,7 @@
// This has changed the visibility of windows, so perform
// a new layout to get them all up-to-date.
- mService.getDefaultDisplayContentLocked().layoutNeeded = true;
+ displayContent.layoutNeeded = true;
// TODO(multidisplay): IMEs are only supported on the default display.
if (windows == mService.getDefaultWindowListLocked()
@@ -1207,6 +1230,9 @@
true /*updateInputWindows*/);
mService.mFocusMayChange = false;
mService.notifyActivityDrawnForKeyguard();
+
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+
return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;
}
@@ -1257,26 +1283,6 @@
int layer = -1;
for (int j = 0; j < wtoken.allAppWindows.size(); j++) {
final WindowState win = wtoken.allAppWindows.get(j);
- // Clearing the mAnimatingExit flag before entering animation. It will be set to true
- // if app window is removed, or window relayout to invisible. We don't want to
- // clear it out for windows that get replaced, because the animation depends on
- // the flag to remove the replaced window.
- //
- // We also don't clear the mAnimatingExit flag for windows which have the
- // mRemoveOnExit flag. This indicates an explicit remove request has been issued
- // by the client. We should let animation proceed and not clear this flag or
- // they won't eventually be removed by WindowStateAnimator#finishExit.
- if (!win.mWillReplaceWindow && !win.mRemoveOnExit) {
- win.mAnimatingExit = false;
- // Clear mAnimating flag together with mAnimatingExit. When animation
- // changes from exiting to entering, we need to clear this flag until the
- // new animation gets applied, so that isAnimationStarting() becomes true
- // until then.
- // Otherwise applySurfaceChangesTransaction will faill to skip surface
- // placement for this window during this period, one or more frame will
- // show up with wrong position or scale.
- win.mWinAnimator.mAnimating = false;
- }
if (win.mWinAnimator.mAnimLayer > layer) {
layer = win.mWinAnimator.mAnimLayer;
}
@@ -1477,7 +1483,7 @@
mObscured = true;
}
- if (w.mHasSurface) {
+ if (w.mHasSurface && canBeSeen) {
if ((attrFlags&FLAG_KEEP_SCREEN_ON) != 0) {
mHoldScreen = w.mSession;
mHoldScreenWindow = w;
@@ -1500,43 +1506,39 @@
}
final int type = attrs.type;
- if (canBeSeen
- && (type == TYPE_SYSTEM_DIALOG
- || type == TYPE_SYSTEM_ERROR
- || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0)) {
+ if (type == TYPE_SYSTEM_DIALOG || type == TYPE_SYSTEM_ERROR
+ || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
mSyswin = true;
}
- if (canBeSeen) {
- // This function assumes that the contents of the default display are
- // processed first before secondary displays.
- final DisplayContent displayContent = w.getDisplayContent();
- if (displayContent != null && displayContent.isDefaultDisplay) {
- // While a dream or keyguard is showing, obscure ordinary application
- // content on secondary displays (by forcibly enabling mirroring unless
- // there is other content we want to show) but still allow opaque
- // keyguard dialogs to be shown.
- if (type == TYPE_DREAM || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
- mObscureApplicationContentOnSecondaryDisplays = true;
- }
- mDisplayHasContent = true;
- } else if (displayContent != null &&
- (!mObscureApplicationContentOnSecondaryDisplays
- || (mObscured && type == TYPE_KEYGUARD_DIALOG))) {
- // Allow full screen keyguard presentation dialogs to be seen.
- mDisplayHasContent = true;
+ // This function assumes that the contents of the default display are
+ // processed first before secondary displays.
+ final DisplayContent displayContent = w.getDisplayContent();
+ if (displayContent != null && displayContent.isDefaultDisplay) {
+ // While a dream or keyguard is showing, obscure ordinary application
+ // content on secondary displays (by forcibly enabling mirroring unless
+ // there is other content we want to show) but still allow opaque
+ // keyguard dialogs to be shown.
+ if (type == TYPE_DREAM || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
+ mObscureApplicationContentOnSecondaryDisplays = true;
}
- if (mPreferredRefreshRate == 0
- && w.mAttrs.preferredRefreshRate != 0) {
- mPreferredRefreshRate = w.mAttrs.preferredRefreshRate;
- }
- if (mPreferredModeId == 0
- && w.mAttrs.preferredDisplayModeId != 0) {
- mPreferredModeId = w.mAttrs.preferredDisplayModeId;
- }
- if ((privateflags & PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE) != 0) {
- mSustainedPerformanceModeCurrent = true;
- }
+ mDisplayHasContent = true;
+ } else if (displayContent != null &&
+ (!mObscureApplicationContentOnSecondaryDisplays
+ || (mObscured && type == TYPE_KEYGUARD_DIALOG))) {
+ // Allow full screen keyguard presentation dialogs to be seen.
+ mDisplayHasContent = true;
+ }
+ if (mPreferredRefreshRate == 0
+ && w.mAttrs.preferredRefreshRate != 0) {
+ mPreferredRefreshRate = w.mAttrs.preferredRefreshRate;
+ }
+ if (mPreferredModeId == 0
+ && w.mAttrs.preferredDisplayModeId != 0) {
+ mPreferredModeId = w.mAttrs.preferredDisplayModeId;
+ }
+ if ((privateflags & PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE) != 0) {
+ mSustainedPerformanceModeCurrent = true;
}
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b528016..e828650 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -218,6 +218,8 @@
private static final String ATTR_SETUP_COMPLETE = "setup-complete";
private static final String ATTR_PROVISIONING_STATE = "provisioning-state";
private static final String ATTR_PERMISSION_POLICY = "permission-policy";
+ private static final String ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED =
+ "device-provisioning-config-applied";
private static final String ATTR_DELEGATED_CERT_INSTALLER = "delegated-cert-installer";
private static final String ATTR_APPLICATION_RESTRICTIONS_MANAGER
@@ -417,6 +419,8 @@
int mUserProvisioningState;
int mPermissionPolicy;
+ boolean mDeviceProvisioningConfigApplied = false;
+
final ArrayMap<ComponentName, ActiveAdmin> mAdminMap = new ArrayMap<>();
final ArrayList<ActiveAdmin> mAdminList = new ArrayList<>();
final ArrayList<ComponentName> mRemovingAdmins = new ArrayList<>();
@@ -2089,8 +2093,9 @@
void removeActiveAdminLocked(final ComponentName adminReceiver, final int userHandle) {
final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
- if (admin != null) {
- getUserData(userHandle).mRemovingAdmins.add(adminReceiver);
+ DevicePolicyData policy = getUserData(userHandle);
+ if (admin != null && !policy.mRemovingAdmins.contains(adminReceiver)) {
+ policy.mRemovingAdmins.add(adminReceiver);
sendAdminCommandLocked(admin,
DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED,
new BroadcastReceiver() {
@@ -2173,6 +2178,10 @@
out.attribute(null, ATTR_SETUP_COMPLETE,
Boolean.toString(true));
}
+ if (policy.mDeviceProvisioningConfigApplied) {
+ out.attribute(null, ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED,
+ Boolean.toString(true));
+ }
if (policy.mUserProvisioningState != DevicePolicyManager.STATE_USER_UNMANAGED) {
out.attribute(null, ATTR_PROVISIONING_STATE,
Integer.toString(policy.mUserProvisioningState));
@@ -2333,6 +2342,12 @@
if (userSetupComplete != null && Boolean.toString(true).equals(userSetupComplete)) {
policy.mUserSetupComplete = true;
}
+ String deviceProvisioningConfigApplied = parser.getAttributeValue(null,
+ ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED);
+ if (deviceProvisioningConfigApplied != null
+ && Boolean.toString(true).equals(deviceProvisioningConfigApplied)) {
+ policy.mDeviceProvisioningConfigApplied = true;
+ }
String provisioningState = parser.getAttributeValue(null, ATTR_PROVISIONING_STATE);
if (!TextUtils.isEmpty(provisioningState)) {
policy.mUserProvisioningState = Integer.parseInt(provisioningState);
@@ -4427,6 +4442,7 @@
intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_URI, uri);
intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_ALIAS, alias);
intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_RESPONSE, response);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
final long id = mInjector.binderClearCallingIdentity();
try {
@@ -5096,10 +5112,6 @@
boolean legacyApp = false;
if (ai.targetSdkVersion <= Build.VERSION_CODES.M) {
legacyApp = true;
- } else if ("com.google.android.apps.enterprise.dmagent".equals(ai.packageName)
- && ai.versionCode == 697) {
- // TODO: STOPSHIP remove this (revert ag/895987) once a new prebuilt is dropped
- legacyApp = true;
}
final int rawStatus = getEncryptionStatus();
@@ -5695,7 +5707,8 @@
}
synchronized (this) {
enforceCanSetDeviceOwnerLocked(userId);
- if (getActiveAdminUncheckedLocked(admin, userId) == null) {
+ if (getActiveAdminUncheckedLocked(admin, userId) == null
+ || getUserData(userId).mRemovingAdmins.contains(admin)) {
throw new IllegalArgumentException("Not active admin: " + admin);
}
@@ -5883,7 +5896,8 @@
synchronized (this) {
enforceCanSetProfileOwnerLocked(userHandle);
- if (getActiveAdminUncheckedLocked(who, userHandle) == null) {
+ if (getActiveAdminUncheckedLocked(who, userHandle) == null
+ || getUserData(userHandle).mRemovingAdmins.contains(who)) {
throw new IllegalArgumentException("Not active admin: " + who);
}
@@ -7919,17 +7933,7 @@
Preconditions.checkNotNull(who, "ComponentName is null");
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- int userId = UserHandle.getCallingUserId();
- long identity = mInjector.binderClearCallingIdentity();
- try {
- IAudioService iAudioService = IAudioService.Stub.asInterface(
- ServiceManager.getService(Context.AUDIO_SERVICE));
- iAudioService.setMasterMute(on, 0, mContext.getPackageName(), userId);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Failed to setMasterMute", re);
- } finally {
- mInjector.binderRestoreCallingIdentity(identity);
- }
+ setUserRestriction(who, UserManager.DISALLLOW_UNMUTE_DEVICE, on);
}
}
@@ -8193,6 +8197,12 @@
@Override
public SystemUpdatePolicy getSystemUpdatePolicy() {
+ if (UserManager.isDeviceInDemoMode(mContext)) {
+ // Pretending to have an automatic update policy when the device is in retail demo
+ // mode. This will allow the device to download and install an ota without
+ // any user interaction.
+ return SystemUpdatePolicy.createAutomaticInstallPolicy();
+ }
synchronized (this) {
SystemUpdatePolicy policy = mOwners.getSystemUpdatePolicy();
if (policy != null && !policy.isValid()) {
@@ -8968,6 +8978,11 @@
}
}
+ @Override
+ public boolean isDeviceProvisioned() {
+ return !TextUtils.isEmpty(mInjector.systemPropertiesGet(PROPERTY_DEVICE_OWNER_PRESENT));
+ }
+
private void removePackageIfRequired(final String packageName, final int userId) {
if (!packageHasActiveAdmins(packageName, userId)) {
// Will not do anything if uninstall was not requested or was already started.
@@ -9034,4 +9049,23 @@
// restrictions.
pushUserRestrictions(userHandle);
}
+
+ @Override
+ public void setDeviceProvisioningConfigApplied() {
+ enforceManageUsers();
+ synchronized (this) {
+ DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
+ policy.mDeviceProvisioningConfigApplied = true;
+ saveSettingsLocked(UserHandle.USER_SYSTEM);
+ }
+ }
+
+ @Override
+ public boolean isDeviceProvisioningConfigApplied() {
+ enforceManageUsers();
+ synchronized (this) {
+ final DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
+ return policy.mDeviceProvisioningConfigApplied;
+ }
+ }
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f59b2ff..97a829e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -42,13 +42,16 @@
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
+import android.os.UserManager;
import android.os.storage.IMountService;
+import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Slog;
import android.view.WindowManager;
import com.android.internal.R;
+import com.android.internal.app.NightDisplayController;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.SamplingProfilerIntegration;
import com.android.internal.os.ZygoteInit;
@@ -61,6 +64,7 @@
import com.android.server.connectivity.MetricsLoggerService;
import com.android.server.devicepolicy.DevicePolicyManagerService;
import com.android.server.display.DisplayManagerService;
+import com.android.server.display.NightDisplayService;
import com.android.server.dreams.DreamManagerService;
import com.android.server.fingerprint.FingerprintService;
import com.android.server.hdmi.HdmiControlService;
@@ -85,6 +89,7 @@
import com.android.server.power.PowerManagerService;
import com.android.server.power.ShutdownThread;
import com.android.server.restrictions.RestrictionsManagerService;
+import com.android.server.retaildemo.RetailDemoModeService;
import com.android.server.soundtrigger.SoundTriggerService;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.storage.DeviceStorageMonitorService;
@@ -154,6 +159,10 @@
"com.google.android.clockwork.ThermalObserver";
private static final String WEAR_BLUETOOTH_SERVICE_CLASS =
"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_TIME_SERVICE_CLASS =
+ "com.google.android.clockwork.time.WearTimeService";
private static final String ACCOUNT_SERVICE_CLASS =
"com.android.server.accounts.AccountManagerService$Lifecycle";
private static final String CONTENT_SERVICE_CLASS =
@@ -176,7 +185,7 @@
* visual content.
*/
private static final int DEFAULT_SYSTEM_THEME =
- com.android.internal.R.style.Theme_DeviceDefault_Light_DarkActionBar;
+ com.android.internal.R.style.Theme_DeviceDefault_System;
private final int mFactoryTestMode;
private Timer mProfilerSnapshotTimer;
@@ -719,14 +728,6 @@
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
- try {
- ActivityManagerNative.getDefault().showBootMessage(
- context.getResources().getText(
- com.android.internal.R.string.android_upgrading_starting_apps),
- false);
- } catch (RemoteException e) {
- }
-
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
if (!disableNonCoreServices) {
traceBeginAndSlog("StartLockSettingsService");
@@ -808,10 +809,8 @@
traceBeginAndSlog("StartNetworkPolicyManagerService");
try {
- networkPolicy = new NetworkPolicyManagerService(
- context, mActivityManagerService,
- (IPowerManager)ServiceManager.getService(Context.POWER_SERVICE),
- networkStats, networkManagement);
+ networkPolicy = new NetworkPolicyManagerService(context,
+ mActivityManagerService, networkStats, networkManagement);
ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy);
} catch (Throwable e) {
reportWtf("starting NetworkPolicy Service", e);
@@ -998,6 +997,10 @@
mSystemServiceManager.startService(TwilightService.class);
+ if (NightDisplayController.isAvailable(context)) {
+ mSystemServiceManager.startService(NightDisplayService.class);
+ }
+
mSystemServiceManager.startService(JobSchedulerService.class);
mSystemServiceManager.startService(SoundTriggerService.class);
@@ -1160,6 +1163,10 @@
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
mSystemServiceManager.startService(WEAR_BLUETOOTH_SERVICE_CLASS);
+ mSystemServiceManager.startService(WEAR_WIFI_MEDIATOR_SERVICE_CLASS);
+ if (!disableNonCoreServices) {
+ mSystemServiceManager.startService(WEAR_TIME_SERVICE_CLASS);
+ }
}
// Before things start rolling, be sure we have decided whether
@@ -1177,6 +1184,11 @@
// MMS service broker
mmsService = mSystemServiceManager.startService(MmsServiceBroker.class);
+ if (Settings.Global.getInt(mContentResolver, Settings.Global.DEVICE_PROVISIONED, 0) == 0 ||
+ UserManager.isDeviceInDemoMode(mSystemContext)) {
+ mSystemServiceManager.startService(RetailDemoModeService.class);
+ }
+
// It is now time to start up the app processes...
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeVibratorServiceReady");
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index bae889c..4bb0902 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -18,15 +18,21 @@
import static android.system.OsConstants.*;
+import android.os.SystemClock;
import android.net.LinkProperties;
import android.net.NetworkUtils;
import android.net.apf.ApfGenerator;
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
import android.net.ip.IpManager;
+import android.net.metrics.ApfProgramEvent;
+import android.net.metrics.ApfStats;
+import android.net.metrics.IpConnectivityLog;
+import android.net.metrics.RaEvent;
import android.system.ErrnoException;
import android.system.Os;
import android.system.PacketSocketAddress;
+import android.text.format.DateUtils;
import android.util.Log;
import android.util.Pair;
@@ -69,6 +75,17 @@
* @hide
*/
public class ApfFilter {
+
+ // Enums describing the outcome of receiving an RA packet.
+ private static enum ProcessRaResult {
+ MATCH, // Received RA matched a known RA
+ DROPPED, // Received RA ignored due to MAX_RAS
+ PARSE_ERROR, // Received RA could not be parsed
+ ZERO_LIFETIME, // Received RA had 0 lifetime
+ UPDATE_NEW_RA, // APF program updated for new RA
+ UPDATE_EXPIRY // APF program updated for expiry
+ }
+
// Thread to listen for RAs.
@VisibleForTesting
class ReceiveThread extends Thread {
@@ -76,6 +93,16 @@
private final FileDescriptor mSocket;
private volatile boolean mStopped;
+ // Starting time of the RA receiver thread.
+ private final long mStart = SystemClock.elapsedRealtime();
+
+ private int mReceivedRas; // Number of received RAs
+ private int mMatchingRas; // Number of received RAs matching a known RA
+ private int mDroppedRas; // Number of received RAs ignored due to the MAX_RAS limit
+ private int mParseErrors; // Number of received RAs that could not be parsed
+ private int mZeroLifetimeRas; // Number of received RAs with a 0 lifetime
+ private int mProgramUpdates; // Number of APF program updates triggered by receiving a RA
+
public ReceiveThread(FileDescriptor socket) {
mSocket = socket;
}
@@ -94,13 +121,46 @@
while (!mStopped) {
try {
int length = Os.read(mSocket, mPacket, 0, mPacket.length);
- processRa(mPacket, length);
+ updateStats(processRa(mPacket, length));
} catch (IOException|ErrnoException e) {
if (!mStopped) {
Log.e(TAG, "Read error", e);
}
}
}
+ logStats();
+ }
+
+ private void updateStats(ProcessRaResult result) {
+ mReceivedRas++;
+ switch(result) {
+ case MATCH:
+ mMatchingRas++;
+ return;
+ case DROPPED:
+ mDroppedRas++;
+ return;
+ case PARSE_ERROR:
+ mParseErrors++;
+ return;
+ case ZERO_LIFETIME:
+ mZeroLifetimeRas++;
+ return;
+ case UPDATE_EXPIRY:
+ mMatchingRas++;
+ mProgramUpdates++;
+ return;
+ case UPDATE_NEW_RA:
+ mProgramUpdates++;
+ return;
+ }
+ }
+
+ private void logStats() {
+ long durationMs = SystemClock.elapsedRealtime() - mStart;
+ int maxSize = mApfCapabilities.maximumApfProgramSize;
+ mMetricsLog.log(new ApfStats(durationMs, mReceivedRas, mMatchingRas, mDroppedRas,
+ mZeroLifetimeRas, mParseErrors, mProgramUpdates, maxSize));
}
}
@@ -120,6 +180,7 @@
private static final int IPV4_FRAGMENT_OFFSET_MASK = 0x1fff;
private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
+ private static final int IPV4_ANY_HOST_ADDRESS = 0;
private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
@@ -141,19 +202,22 @@
// NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28;
- private static int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
- private static final byte[] ARP_IPV4_REQUEST_HEADER = new byte[]{
+ private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
+ private static final int ARP_OPCODE_OFFSET = ARP_HEADER_OFFSET + 6;
+ private static final short ARP_OPCODE_REQUEST = 1;
+ private static final short ARP_OPCODE_REPLY = 2;
+ private static final byte[] ARP_IPV4_HEADER = new byte[]{
0, 1, // Hardware type: Ethernet (1)
8, 0, // Protocol type: IP (0x0800)
6, // Hardware size: 6
4, // Protocol size: 4
- 0, 1 // Opcode: request (1)
};
- private static int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
+ private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
private final ApfCapabilities mApfCapabilities;
private final IpManager.Callback mIpManagerCallback;
private final NetworkInterface mNetworkInterface;
+ private final IpConnectivityLog mMetricsLog;
@VisibleForTesting
byte[] mHardwareAddress;
@VisibleForTesting
@@ -168,11 +232,12 @@
@VisibleForTesting
ApfFilter(ApfCapabilities apfCapabilities, NetworkInterface networkInterface,
- IpManager.Callback ipManagerCallback, boolean multicastFilter) {
+ IpManager.Callback ipManagerCallback, boolean multicastFilter, IpConnectivityLog log) {
mApfCapabilities = apfCapabilities;
mIpManagerCallback = ipManagerCallback;
mNetworkInterface = networkInterface;
mMulticastFilter = multicastFilter;
+ mMetricsLog = log;
maybeStartFilter();
}
@@ -213,8 +278,9 @@
}
// Returns seconds since Unix Epoch.
+ // TODO: use SystemClock.elapsedRealtime() instead
private static long curTime() {
- return System.currentTimeMillis() / 1000L;
+ return System.currentTimeMillis() / DateUtils.SECOND_IN_MILLIS;
}
// A class to hold information about an RA.
@@ -297,7 +363,7 @@
}
// Can't be static because it's in a non-static inner class.
- // TODO: Make this final once RA is its own class.
+ // TODO: Make this static once RA is its own class.
private int uint8(byte b) {
return b & 0xff;
}
@@ -306,14 +372,18 @@
return s & 0xffff;
}
- private long uint32(int s) {
- return s & 0xffffffffL;
+ private long uint32(int i) {
+ return i & 0xffffffffL;
}
private long getUint16(ByteBuffer buffer, int position) {
return uint16(buffer.getShort(position));
}
+ private long getUint32(ByteBuffer buffer, int position) {
+ return uint32(buffer.getInt(position));
+ }
+
private void prefixOptionToString(StringBuffer sb, int offset) {
String prefix = IPv6AddresstoString(offset + 16);
int length = uint8(mPacket.get(offset + 2));
@@ -360,7 +430,7 @@
* @param lifetimeOffset offset from mPacket.position() to the next lifetime data.
* @param lifetimeLength length of the next lifetime data.
* @return offset within packet of where the next binary range of data not including
- * a lifetime. This can be passed into the next invocation of this function
+ * a lifetime. This can be passed into the next invocation of this function
* via {@code lastNonLifetimeStart}.
*/
private int addNonLifetime(int lastNonLifetimeStart, int lifetimeOffset,
@@ -371,13 +441,17 @@
return lifetimeOffset + lifetimeLength;
}
+ private int addNonLifetimeU32(int lastNonLifetimeStart) {
+ return addNonLifetime(lastNonLifetimeStart,
+ ICMP6_4_BYTE_LIFETIME_OFFSET, ICMP6_4_BYTE_LIFETIME_LEN);
+ }
+
// Note that this parses RA and may throw IllegalArgumentException (from
// Buffer.position(int) or due to an invalid-length option) or IndexOutOfBoundsException
// (from ByteBuffer.get(int) ) if parsing encounters something non-compliant with
// specifications.
Ra(byte[] packet, int length) {
- mPacket = ByteBuffer.allocate(length).put(ByteBuffer.wrap(packet, 0, length));
- mPacket.clear();
+ mPacket = ByteBuffer.wrap(Arrays.copyOf(packet, length));
mLastSeen = curTime();
// Sanity check packet in case a packet arrives before we attach RA filter
@@ -389,6 +463,8 @@
}
+ RaEvent.Builder builder = new RaEvent.Builder();
+
// Ignore the checksum.
int lastNonLifetimeStart = addNonLifetime(0,
ICMP6_RA_CHECKSUM_OFFSET,
@@ -398,35 +474,50 @@
lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
ICMP6_RA_ROUTER_LIFETIME_OFFSET,
ICMP6_RA_ROUTER_LIFETIME_LEN);
+ builder.updateRouterLifetime(getUint16(mPacket, ICMP6_RA_ROUTER_LIFETIME_OFFSET));
// Ensures that the RA is not truncated.
mPacket.position(ICMP6_RA_OPTION_OFFSET);
while (mPacket.hasRemaining()) {
- int optionType = ((int)mPacket.get(mPacket.position())) & 0xff;
- int optionLength = (((int)mPacket.get(mPacket.position() + 1)) & 0xff) * 8;
+ final int position = mPacket.position();
+ final int optionType = uint8(mPacket.get(position));
+ final int optionLength = uint8(mPacket.get(position + 1)) * 8;
+ long lifetime;
switch (optionType) {
case ICMP6_PREFIX_OPTION_TYPE:
// Parse valid lifetime
lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET,
ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN);
+ lifetime = getUint32(mPacket,
+ position + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET);
+ builder.updatePrefixValidLifetime(lifetime);
// Parse preferred lifetime
lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET,
ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN);
- mPrefixOptionOffsets.add(mPacket.position());
+ lifetime = getUint32(mPacket,
+ position + ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET);
+ builder.updatePrefixPreferredLifetime(lifetime);
+ mPrefixOptionOffsets.add(position);
break;
- // These three options have the same lifetime offset and size, so process
- // together:
+ // These three options have the same lifetime offset and size, and
+ // are processed with the same specialized addNonLifetimeU32:
case ICMP6_RDNSS_OPTION_TYPE:
- mRdnssOptionOffsets.add(mPacket.position());
- // Fall through.
+ mRdnssOptionOffsets.add(position);
+ lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
+ lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
+ builder.updateRdnssLifetime(lifetime);
+ break;
case ICMP6_ROUTE_INFO_OPTION_TYPE:
+ lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
+ lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
+ builder.updateRouteInfoLifetime(lifetime);
+ break;
case ICMP6_DNSSL_OPTION_TYPE:
- // Parse lifetime
- lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
- ICMP6_4_BYTE_LIFETIME_OFFSET,
- ICMP6_4_BYTE_LIFETIME_LEN);
+ lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
+ lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
+ builder.updateDnsslLifetime(lifetime);
break;
default:
// RFC4861 section 4.2 dictates we ignore unknown options for fowards
@@ -437,11 +528,12 @@
throw new IllegalArgumentException(String.format(
"Invalid option length opt=%d len=%d", optionType, optionLength));
}
- mPacket.position(mPacket.position() + optionLength);
+ mPacket.position(position + optionLength);
}
// Mark non-lifetime bytes since last lifetime.
addNonLifetime(lastNonLifetimeStart, 0, 0);
mMinLifetime = minLifetime(packet, length);
+ mMetricsLog.log(builder.build());
}
// Ignoring lifetimes (which may change) does {@code packet} match this RA?
@@ -470,16 +562,19 @@
continue;
}
- int lifetimeLength = mNonLifetimes.get(i+1).first - offset;
- long val;
+ final int lifetimeLength = mNonLifetimes.get(i+1).first - offset;
+ final long optionLifetime;
switch (lifetimeLength) {
- case 2: val = byteBuffer.getShort(offset); break;
- case 4: val = byteBuffer.getInt(offset); break;
- default: throw new IllegalStateException("bogus lifetime size " + length);
+ case 2:
+ optionLifetime = uint16(byteBuffer.getShort(offset));
+ break;
+ case 4:
+ optionLifetime = uint32(byteBuffer.getInt(offset));
+ break;
+ default:
+ throw new IllegalStateException("bogus lifetime size " + lifetimeLength);
}
- // Mask to size, converting signed to unsigned
- val &= (1L << (lifetimeLength * 8)) - 1;
- minLifetime = Math.min(minLifetime, val);
+ minLifetime = Math.min(minLifetime, optionLifetime);
}
return minLifetime;
}
@@ -586,23 +681,48 @@
private void generateArpFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
// Here's a basic summary of what the ARP filter program does:
//
- // if interface has IPv4 address:
- // if it's not an ARP IPv4 request:
- // pass
- // if it's not a request for our IPv4 address:
- // drop
+ // if not ARP IPv4
+ // pass
+ // if not ARP IPv4 reply or request
+ // pass
+ // if unicast ARP reply
+ // pass
+ // if interface has no IPv4 address
+ // if target ip is 0.0.0.0
+ // drop
+ // else
+ // if target ip is not the interface ip
+ // drop
// pass
- if (mIPv4Address != null) {
- // if it's not an ARP IPv4 request, pass
- gen.addLoadImmediate(Register.R0, ARP_HEADER_OFFSET);
- gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_REQUEST_HEADER, gen.PASS_LABEL);
- // if it's not a request for our IPv4 address, drop
+ final String checkTargetIPv4 = "checkTargetIPv4";
+
+ // Pass if not ARP IPv4.
+ gen.addLoadImmediate(Register.R0, ARP_HEADER_OFFSET);
+ gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_HEADER, gen.PASS_LABEL);
+
+ // Pass if unknown ARP opcode.
+ gen.addLoad16(Register.R0, ARP_OPCODE_OFFSET);
+ gen.addJumpIfR0Equals(ARP_OPCODE_REQUEST, checkTargetIPv4); // Skip to unicast check
+ gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, gen.PASS_LABEL);
+
+ // Pass if unicast reply.
+ gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
+ gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
+
+ // Either a unicast request, a unicast reply, or a broadcast reply.
+ gen.defineLabel(checkTargetIPv4);
+ if (mIPv4Address == null) {
+ // When there is no IPv4 address, drop GARP replies (b/29404209).
+ gen.addLoad32(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET);
+ gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, gen.DROP_LABEL);
+ } else {
+ // When there is an IPv4 address, drop unicast/broadcast requests
+ // and broadcast replies with a different target IPv4 address.
gen.addLoadImmediate(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET);
gen.addJumpIfBytesNotEqual(Register.R0, mIPv4Address, gen.DROP_LABEL);
}
- // Otherwise, pass
gen.addJump(gen.PASS_LABEL);
}
@@ -774,16 +894,19 @@
return gen;
}
+ /**
+ * Generate and install a new filter program.
+ */
@GuardedBy("this")
@VisibleForTesting
void installNewProgramLocked() {
purgeExpiredRasLocked();
+ ArrayList<Ra> rasToFilter = new ArrayList<>();
final byte[] program;
long programMinLifetime = Long.MAX_VALUE;
try {
// Step 1: Determine how many RA filters we can fit in the program.
ApfGenerator gen = beginProgramLocked();
- ArrayList<Ra> rasToFilter = new ArrayList<Ra>();
for (Ra ra : mRas) {
ra.generateFilterLocked(gen);
// Stop if we get too big.
@@ -811,17 +934,17 @@
hexDump("Installing filter: ", program, program.length);
}
mIpManagerCallback.installPacketFilter(program);
+ int flags = ApfProgramEvent.flagsFor(mIPv4Address != null, mMulticastFilter);
+ mMetricsLog.log(new ApfProgramEvent(
+ programMinLifetime, rasToFilter.size(), mRas.size(), program.length, flags));
}
- // Install a new filter program if the last installed one will die soon.
- @GuardedBy("this")
- private void maybeInstallNewProgramLocked() {
- if (mRas.size() == 0) return;
- // If the current program doesn't expire for a while, don't bother updating.
+ /**
+ * Returns {@code true} if a new program should be installed because the current one dies soon.
+ */
+ private boolean shouldInstallnewProgram() {
long expiry = mLastTimeInstalledProgram + mLastInstalledProgramMinLifetime;
- if (expiry < curTime() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING) {
- installNewProgramLocked();
- }
+ return expiry < curTime() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING;
}
private void hexDump(String msg, byte[] packet, int length) {
@@ -840,7 +963,12 @@
}
}
- private synchronized void processRa(byte[] packet, int length) {
+ /**
+ * Process an RA packet, updating the list of known RAs and installing a new APF program
+ * if the current APF program should be updated.
+ * @return a ProcessRaResult enum describing what action was performed.
+ */
+ private synchronized ProcessRaResult processRa(byte[] packet, int length) {
if (VDBG) hexDump("Read packet = ", packet, length);
// Have we seen this RA before?
@@ -862,25 +990,34 @@
// Swap to front of array.
mRas.add(0, mRas.remove(i));
- maybeInstallNewProgramLocked();
- return;
+ // If the current program doesn't expire for a while, don't update.
+ if (shouldInstallnewProgram()) {
+ installNewProgramLocked();
+ return ProcessRaResult.UPDATE_EXPIRY;
+ }
+ return ProcessRaResult.MATCH;
}
}
purgeExpiredRasLocked();
// TODO: figure out how to proceed when we've received more then MAX_RAS RAs.
- if (mRas.size() >= MAX_RAS) return;
+ if (mRas.size() >= MAX_RAS) {
+ return ProcessRaResult.DROPPED;
+ }
final Ra ra;
try {
ra = new Ra(packet, length);
} catch (Exception e) {
Log.e(TAG, "Error parsing RA: " + e);
- return;
+ return ProcessRaResult.PARSE_ERROR;
}
// Ignore 0 lifetime RAs.
- if (ra.isExpired()) return;
+ if (ra.isExpired()) {
+ return ProcessRaResult.ZERO_LIFETIME;
+ }
log("Adding " + ra);
mRas.add(ra);
installNewProgramLocked();
+ return ProcessRaResult.UPDATE_NEW_RA;
}
/**
@@ -905,7 +1042,8 @@
Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
return null;
}
- return new ApfFilter(apfCapabilities, networkInterface, ipManagerCallback, multicastFilter);
+ return new ApfFilter(apfCapabilities, networkInterface, ipManagerCallback,
+ multicastFilter, new IpConnectivityLog());
}
public synchronized void shutdown() {
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 96c852b..ffbea9f 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -30,6 +30,7 @@
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
import android.net.NetworkUtils;
+import android.net.metrics.IpConnectivityLog;
import android.net.metrics.DhcpClientEvent;
import android.net.metrics.DhcpErrorEvent;
import android.os.Message;
@@ -163,6 +164,7 @@
// System services / libraries we use.
private final Context mContext;
private final Random mRandom;
+ private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
// Sockets.
// - We use a packet socket to receive, because servers send us packets bound for IP addresses
@@ -192,6 +194,10 @@
private long mDhcpLeaseExpiry;
private DhcpResults mOffer;
+ // Milliseconds SystemClock timestamps used to record transition times to DhcpBoundState.
+ private long mLastInitEnterTime;
+ private long mLastBoundExitTime;
+
// States.
private State mStoppedState = new StoppedState();
private State mDhcpState = new DhcpState();
@@ -356,14 +362,14 @@
} catch (IOException|ErrnoException e) {
if (!mStopped) {
Log.e(TAG, "Read error", e);
- DhcpErrorEvent.logReceiveError(mIfaceName);
+ logError(DhcpErrorEvent.RECEIVE_ERROR);
}
} catch (DhcpPacket.ParseException e) {
Log.e(TAG, "Can't parse packet: " + e.getMessage());
if (PACKET_DBG) {
Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length));
}
- DhcpErrorEvent.logParseError(mIfaceName, e.errorCode);
+ logError(e.errorCode);
}
}
if (DBG) Log.d(TAG, "Receive thread stopped");
@@ -490,10 +496,18 @@
}
abstract class LoggingState extends State {
+ private long mEnterTimeMs;
+
@Override
public void enter() {
if (STATE_DBG) Log.d(TAG, "Entering state " + getName());
- DhcpClientEvent.logStateEvent(mIfaceName, getName());
+ mEnterTimeMs = SystemClock.elapsedRealtime();
+ }
+
+ @Override
+ public void exit() {
+ long durationMs = SystemClock.elapsedRealtime() - mEnterTimeMs;
+ logState(getName(), (int) durationMs);
}
private String messageName(int what) {
@@ -518,6 +532,13 @@
}
return NOT_HANDLED;
}
+
+ @Override
+ public String getName() {
+ // All DhcpClient's states are inner classes with a well defined name.
+ // Use getSimpleName() and avoid super's getName() creating new String instances.
+ return getClass().getSimpleName();
+ }
}
// Sends CMD_PRE_DHCP_ACTION to the controller, waits for the controller to respond with
@@ -544,10 +565,9 @@
}
}
- class StoppedState extends LoggingState {
+ class StoppedState extends State {
@Override
public boolean processMessage(Message message) {
- super.processMessage(message);
switch (message.what) {
case CMD_START_DHCP:
if (mRegisteredForPreDhcpNotification) {
@@ -576,10 +596,9 @@
}
}
- class DhcpState extends LoggingState {
+ class DhcpState extends State {
@Override
public void enter() {
- super.enter();
clearDhcpState();
if (initInterface() && initSockets()) {
mReceiveThread = new ReceiveThread();
@@ -677,7 +696,9 @@
}
}
+ @Override
public void exit() {
+ super.exit();
mKickAlarm.cancel();
mTimeoutAlarm.cancel();
}
@@ -724,6 +745,7 @@
public void enter() {
super.enter();
startNewTransaction();
+ mLastInitEnterTime = SystemClock.elapsedRealtime();
}
protected boolean sendPacket() {
@@ -782,15 +804,9 @@
}
}
- class DhcpHaveLeaseState extends LoggingState {
- @Override
- public void enter() {
- super.enter();
- }
-
+ class DhcpHaveLeaseState extends State {
@Override
public boolean processMessage(Message message) {
- super.processMessage(message);
switch (message.what) {
case CMD_EXPIRE_DHCP:
Log.d(TAG, "Lease expired!");
@@ -854,6 +870,13 @@
}
scheduleLeaseTimers();
+ logTimeToBoundState();
+ }
+
+ @Override
+ public void exit() {
+ super.exit();
+ mLastBoundExitTime = SystemClock.elapsedRealtime();
}
@Override
@@ -871,6 +894,15 @@
return NOT_HANDLED;
}
}
+
+ private void logTimeToBoundState() {
+ long now = SystemClock.elapsedRealtime();
+ if (mLastBoundExitTime > mLastInitEnterTime) {
+ logState(DhcpClientEvent.RENEWING_BOUND, (int)(now - mLastBoundExitTime));
+ } else {
+ logState(DhcpClientEvent.INITIAL_BOUND, (int)(now - mLastInitEnterTime));
+ }
+ }
}
abstract class DhcpReacquiringState extends PacketRetransmittingState {
@@ -977,4 +1009,12 @@
class DhcpRebootingState extends LoggingState {
}
+
+ private void logError(int errorCode) {
+ mMetricsLog.log(new DhcpErrorEvent(mIfaceName, errorCode));
+ }
+
+ private void logState(String name, int durationMs) {
+ mMetricsLog.log(new DhcpClientEvent(mIfaceName, name, durationMs));
+ }
}
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index cece6c8..654ef18 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -31,6 +31,7 @@
import android.net.RouteInfo;
import android.net.StaticIpConfiguration;
import android.net.dhcp.DhcpClient;
+import android.net.metrics.IpConnectivityLog;
import android.net.metrics.IpManagerEvent;
import android.os.INetworkManagementService;
import android.os.Message;
@@ -393,6 +394,7 @@
private final WakeupMessage mProvisioningTimeoutAlarm;
private final WakeupMessage mDhcpActionTimeoutAlarm;
private final LocalLog mLocalLog;
+ private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
private NetworkInterface mNetworkInterface;
@@ -634,8 +636,8 @@
private void recordMetric(final int type) {
if (mStartTimeMillis <= 0) { Log.wtf(mTag, "Start time undefined!"); }
- IpManagerEvent.logEvent(type, mInterfaceName,
- SystemClock.elapsedRealtime() - mStartTimeMillis);
+ final long duration = SystemClock.elapsedRealtime() - mStartTimeMillis;
+ mMetricsLog.log(new IpManagerEvent(mInterfaceName, type, duration));
}
// For now: use WifiStateMachine's historical notion of provisioned.
@@ -916,12 +918,6 @@
mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpManager.this, mInterfaceName);
mDhcpClient.registerForPreDhcpNotification();
mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
-
- if (mConfiguration.mProvisioningTimeoutMs > 0) {
- final long alarmTime = SystemClock.elapsedRealtime() +
- mConfiguration.mProvisioningTimeoutMs;
- mProvisioningTimeoutAlarm.schedule(alarmTime);
- }
}
return true;
@@ -1041,11 +1037,24 @@
mCallback.setFallbackMulticastFilter(mMulticastFiltering);
}
+ if (mConfiguration.mProvisioningTimeoutMs > 0) {
+ final long alarmTime = SystemClock.elapsedRealtime() +
+ mConfiguration.mProvisioningTimeoutMs;
+ mProvisioningTimeoutAlarm.schedule(alarmTime);
+ }
+
if (mConfiguration.mEnableIPv6) {
// TODO: Consider transitionTo(mStoppingState) if this fails.
startIPv6();
}
+ if (mConfiguration.mEnableIPv4) {
+ if (!startIPv4()) {
+ transitionTo(mStoppingState);
+ return;
+ }
+ }
+
if (mConfiguration.mUsingIpReachabilityMonitor) {
mIpReachabilityMonitor = new IpReachabilityMonitor(
mContext,
@@ -1057,12 +1066,6 @@
}
});
}
-
- if (mConfiguration.mEnableIPv4) {
- if (!startIPv4()) {
- transitionTo(mStoppingState);
- }
- }
}
@Override
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index 27600a7..c6da3c3 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -24,6 +24,7 @@
import android.net.LinkProperties.ProvisioningChange;
import android.net.ProxyInfo;
import android.net.RouteInfo;
+import android.net.metrics.IpConnectivityLog;
import android.net.metrics.IpReachabilityEvent;
import android.net.netlink.NetlinkConstants;
import android.net.netlink.NetlinkErrorMessage;
@@ -128,7 +129,6 @@
* state it may be best for the link to disconnect completely and
* reconnect afresh.
*
- *
* @hide
*/
public class IpReachabilityMonitor {
@@ -151,6 +151,7 @@
private final Callback mCallback;
private final NetlinkSocketObserver mNetlinkSocketObserver;
private final Thread mObserverThread;
+ private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
@GuardedBy("mLock")
private LinkProperties mLinkProperties = new LinkProperties();
// TODO: consider a map to a private NeighborState class holding more
@@ -161,6 +162,8 @@
private int mIpWatchListVersion;
@GuardedBy("mLock")
private boolean mRunning;
+ // Time in milliseconds of the last forced probe request.
+ private volatile long mLastProbeTimeMs;
/**
* Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
@@ -337,7 +340,7 @@
private void handleNeighborLost(String msg) {
InetAddress ip = null;
- ProvisioningChange delta;
+ final ProvisioningChange delta;
synchronized (mLock) {
LinkProperties whatIfLp = new LinkProperties(mLinkProperties);
@@ -359,7 +362,6 @@
}
if (delta == ProvisioningChange.LOST_PROVISIONING) {
- IpReachabilityEvent.logProvisioningLost(mInterfaceName);
final String logMsg = "FAILURE: LOST_PROVISIONING, " + msg;
Log.w(TAG, logMsg);
if (mCallback != null) {
@@ -367,9 +369,8 @@
// an InetAddress argument.
mCallback.notifyLost(ip, logMsg);
}
- } else {
- IpReachabilityEvent.logNudFailed(mInterfaceName);
}
+ logNudFailed(delta);
}
public void probeAll() {
@@ -393,11 +394,12 @@
break;
}
final int returnValue = probeNeighbor(mInterfaceIndex, target);
- IpReachabilityEvent.logProbeEvent(mInterfaceName, returnValue);
+ logEvent(IpReachabilityEvent.PROBE, returnValue);
}
+ mLastProbeTimeMs = SystemClock.elapsedRealtime();
}
- private long getProbeWakeLockDuration() {
+ private static long getProbeWakeLockDuration() {
// Ideally, this would be computed by examining the values of:
//
// /proc/sys/net/ipv[46]/neigh/<ifname>/ucast_solicit
@@ -413,6 +415,19 @@
return (numUnicastProbes * retransTimeMs) + gracePeriodMs;
}
+ private void logEvent(int probeType, int errorCode) {
+ int eventType = probeType | (errorCode & 0xff);
+ mMetricsLog.log(new IpReachabilityEvent(mInterfaceName, eventType));
+ }
+
+ private void logNudFailed(ProvisioningChange delta) {
+ long duration = SystemClock.elapsedRealtime() - mLastProbeTimeMs;
+ boolean isFromProbe = (duration < getProbeWakeLockDuration());
+ boolean isProvisioningLost = (delta == ProvisioningChange.LOST_PROVISIONING);
+ int eventType = IpReachabilityEvent.nudFailureEventType(isFromProbe, isProvisioningLost);
+ mMetricsLog.log(new IpReachabilityEvent(mInterfaceName, eventType));
+ }
+
// TODO: simplify the number of objects by making this extend Thread.
private final class NetlinkSocketObserver implements Runnable {
private NetlinkSocket mSocket;
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
new file mode 100644
index 0000000..6802cff
--- /dev/null
+++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
@@ -0,0 +1,716 @@
+/*
+ * 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.net.ip;
+
+import static android.system.OsConstants.*;
+
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkUtils;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructGroupReq;
+import android.system.StructTimeval;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import libcore.io.IoBridge;
+import libcore.util.HexEncoding;
+
+import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
+import java.io.IOException;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+
+/**
+ * Basic IPv6 Router Advertisement Daemon.
+ *
+ * TODO:
+ *
+ * - Rewrite using Handler (and friends) so that AlarmManager can deliver
+ * "kick" messages when it's time to send a multicast RA.
+ *
+ * @hide
+ */
+public class RouterAdvertisementDaemon {
+ private static final String TAG = RouterAdvertisementDaemon.class.getSimpleName();
+ private static final byte ICMPV6_ND_ROUTER_SOLICIT = asByte(133);
+ private static final byte ICMPV6_ND_ROUTER_ADVERT = asByte(134);
+ private static final int IPV6_MIN_MTU = 1280;
+ private static final int MIN_RA_HEADER_SIZE = 16;
+
+ // Summary of various timers and lifetimes.
+ private static final int MIN_RTR_ADV_INTERVAL_SEC = 300;
+ private static final int MAX_RTR_ADV_INTERVAL_SEC = 600;
+ // In general, router, prefix, and DNS lifetimes are all advised to be
+ // greater than or equal to 3 * MAX_RTR_ADV_INTERVAL. Here, we double
+ // that to allow for multicast packet loss.
+ //
+ // This MAX_RTR_ADV_INTERVAL_SEC and DEFAULT_LIFETIME are also consistent
+ // with the https://tools.ietf.org/html/rfc7772#section-4 discussion of
+ // "approximately 7 RAs per hour".
+ private static final int DEFAULT_LIFETIME = 6 * MAX_RTR_ADV_INTERVAL_SEC;
+ // From https://tools.ietf.org/html/rfc4861#section-10 .
+ private static final int MIN_DELAY_BETWEEN_RAS_SEC = 3;
+ // Both initial and final RAs, but also for changes in RA contents.
+ // From https://tools.ietf.org/html/rfc4861#section-10 .
+ private static final int MAX_URGENT_RTR_ADVERTISEMENTS = 5;
+
+ private static final int DAY_IN_SECONDS = 86_400;
+
+ private static final byte[] ALL_NODES = new byte[] {
+ (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
+ };
+
+ private final String mIfName;
+ private final int mIfIndex;
+ private final byte[] mHwAddr;
+ private final InetSocketAddress mAllNodes;
+
+ // This lock is to protect the RA from being updated while being
+ // transmitted on another thread (multicast or unicast).
+ //
+ // TODO: This should be handled with a more RCU-like approach.
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private final byte[] mRA = new byte[IPV6_MIN_MTU];
+ @GuardedBy("mLock")
+ private int mRaLength;
+ @GuardedBy("mLock")
+ private final DeprecatedInfoTracker mDeprecatedInfoTracker;
+ @GuardedBy("mLock")
+ private RaParams mRaParams;
+
+ private volatile FileDescriptor mSocket;
+ private volatile MulticastTransmitter mMulticastTransmitter;
+ private volatile UnicastResponder mUnicastResponder;
+
+ public static class RaParams {
+ public boolean hasDefaultRoute;
+ public int mtu;
+ public HashSet<IpPrefix> prefixes;
+ public HashSet<Inet6Address> dnses;
+
+ public RaParams() {
+ hasDefaultRoute = false;
+ mtu = IPV6_MIN_MTU;
+ prefixes = new HashSet<IpPrefix>();
+ dnses = new HashSet<Inet6Address>();
+ }
+
+ public RaParams(RaParams other) {
+ hasDefaultRoute = other.hasDefaultRoute;
+ mtu = other.mtu;
+ prefixes = (HashSet) other.prefixes.clone();
+ dnses = (HashSet) other.dnses.clone();
+ }
+
+ // Returns the subset of RA parameters that become deprecated when
+ // moving from announcing oldRa to announcing newRa.
+ //
+ // Currently only tracks differences in |prefixes| and |dnses|.
+ public static RaParams getDeprecatedRaParams(RaParams oldRa, RaParams newRa) {
+ RaParams newlyDeprecated = new RaParams();
+
+ if (oldRa != null) {
+ for (IpPrefix ipp : oldRa.prefixes) {
+ if (newRa == null || !newRa.prefixes.contains(ipp)) {
+ newlyDeprecated.prefixes.add(ipp);
+ }
+ }
+
+ for (Inet6Address dns : oldRa.dnses) {
+ if (newRa == null || !newRa.dnses.contains(dns)) {
+ newlyDeprecated.dnses.add(dns);
+ }
+ }
+ }
+
+ return newlyDeprecated;
+ }
+ }
+
+ private static class DeprecatedInfoTracker {
+ private final HashMap<IpPrefix, Integer> mPrefixes = new HashMap<>();
+ private final HashMap<Inet6Address, Integer> mDnses = new HashMap<>();
+
+ Set<IpPrefix> getPrefixes() { return mPrefixes.keySet(); }
+
+ void putPrefixes(Set<IpPrefix> prefixes) {
+ for (IpPrefix ipp : prefixes) {
+ mPrefixes.put(ipp, MAX_URGENT_RTR_ADVERTISEMENTS);
+ }
+ }
+
+ void removePrefixes(Set<IpPrefix> prefixes) {
+ for (IpPrefix ipp : prefixes) {
+ mPrefixes.remove(ipp);
+ }
+ }
+
+ Set<Inet6Address> getDnses() { return mDnses.keySet(); }
+
+ void putDnses(Set<Inet6Address> dnses) {
+ for (Inet6Address dns : dnses) {
+ mDnses.put(dns, MAX_URGENT_RTR_ADVERTISEMENTS);
+ }
+ }
+
+ void removeDnses(Set<Inet6Address> dnses) {
+ for (Inet6Address dns : dnses) {
+ mDnses.remove(dns);
+ }
+ }
+
+ boolean isEmpty() { return mPrefixes.isEmpty() && mDnses.isEmpty(); }
+
+ private boolean decrementCounters() {
+ boolean removed = decrementCounter(mPrefixes);
+ removed |= decrementCounter(mDnses);
+ return removed;
+ }
+
+ private <T> boolean decrementCounter(HashMap<T, Integer> map) {
+ boolean removed = false;
+
+ for (Iterator<Map.Entry<T, Integer>> it = map.entrySet().iterator();
+ it.hasNext();) {
+ Map.Entry<T, Integer> kv = it.next();
+ if (kv.getValue() == 0) {
+ it.remove();
+ removed = true;
+ } else {
+ kv.setValue(kv.getValue() - 1);
+ }
+ }
+
+ return removed;
+ }
+ }
+
+
+ public RouterAdvertisementDaemon(String ifname, int ifindex, byte[] hwaddr) {
+ mIfName = ifname;
+ mIfIndex = ifindex;
+ mHwAddr = hwaddr;
+ mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mIfIndex), 0);
+ mDeprecatedInfoTracker = new DeprecatedInfoTracker();
+ }
+
+ public void buildNewRa(RaParams deprecatedParams, RaParams newParams) {
+ synchronized (mLock) {
+ if (deprecatedParams != null) {
+ mDeprecatedInfoTracker.putPrefixes(deprecatedParams.prefixes);
+ mDeprecatedInfoTracker.putDnses(deprecatedParams.dnses);
+ }
+
+ if (newParams != null) {
+ // Process information that is no longer deprecated.
+ mDeprecatedInfoTracker.removePrefixes(newParams.prefixes);
+ mDeprecatedInfoTracker.removeDnses(newParams.dnses);
+ }
+
+ mRaParams = newParams;
+ assembleRaLocked();
+ }
+
+ maybeNotifyMulticastTransmitter();
+ }
+
+ public boolean start() {
+ if (!createSocket()) {
+ return false;
+ }
+
+ mMulticastTransmitter = new MulticastTransmitter();
+ mMulticastTransmitter.start();
+
+ mUnicastResponder = new UnicastResponder();
+ mUnicastResponder.start();
+
+ return true;
+ }
+
+ public void stop() {
+ closeSocket();
+ mMulticastTransmitter = null;
+ mUnicastResponder = null;
+ }
+
+ private void assembleRaLocked() {
+ final ByteBuffer ra = ByteBuffer.wrap(mRA);
+ ra.order(ByteOrder.BIG_ENDIAN);
+
+ boolean shouldSendRA = false;
+
+ try {
+ putHeader(ra, mRaParams != null && mRaParams.hasDefaultRoute);
+ putSlla(ra, mHwAddr);
+ mRaLength = ra.position();
+
+ // https://tools.ietf.org/html/rfc5175#section-4 says:
+ //
+ // "MUST NOT be added to a Router Advertisement message
+ // if no flags in the option are set."
+ //
+ // putExpandedFlagsOption(ra);
+
+ if (mRaParams != null) {
+ putMtu(ra, mRaParams.mtu);
+ mRaLength = ra.position();
+
+ for (IpPrefix ipp : mRaParams.prefixes) {
+ putPio(ra, ipp, DEFAULT_LIFETIME, DEFAULT_LIFETIME);
+ mRaLength = ra.position();
+ shouldSendRA = true;
+ }
+
+ if (mRaParams.dnses.size() > 0) {
+ putRdnss(ra, mRaParams.dnses, DEFAULT_LIFETIME);
+ mRaLength = ra.position();
+ shouldSendRA = true;
+ }
+ }
+
+ for (IpPrefix ipp : mDeprecatedInfoTracker.getPrefixes()) {
+ putPio(ra, ipp, 0, 0);
+ mRaLength = ra.position();
+ shouldSendRA = true;
+ }
+
+ final Set<Inet6Address> deprecatedDnses = mDeprecatedInfoTracker.getDnses();
+ if (!deprecatedDnses.isEmpty()) {
+ putRdnss(ra, deprecatedDnses, 0);
+ mRaLength = ra.position();
+ shouldSendRA = true;
+ }
+ } catch (BufferOverflowException e) {
+ // The packet up to mRaLength is valid, since it has been updated
+ // progressively as the RA was built. Log an error, and continue
+ // on as best as possible.
+ Log.e(TAG, "Could not construct new RA: " + e);
+ }
+
+ // We have nothing worth announcing; indicate as much to maybeSendRA().
+ if (!shouldSendRA) {
+ mRaLength = 0;
+ }
+ }
+
+ private void maybeNotifyMulticastTransmitter() {
+ final MulticastTransmitter m = mMulticastTransmitter;
+ if (m != null) {
+ m.hup();
+ }
+ }
+
+ private static Inet6Address getAllNodesForScopeId(int scopeId) {
+ try {
+ return Inet6Address.getByAddress("ff02::1", ALL_NODES, scopeId);
+ } catch (UnknownHostException uhe) {
+ Log.wtf(TAG, "Failed to construct ff02::1 InetAddress: " + uhe);
+ return null;
+ }
+ }
+
+ private static byte asByte(int value) { return (byte) value; }
+ private static short asShort(int value) { return (short) value; }
+
+ private static void putHeader(ByteBuffer ra, boolean hasDefaultRoute) {
+ /**
+ Router Advertisement Message Format
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Type | Code | Checksum |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Cur Hop Limit |M|O|H|Prf|P|R|R| Router Lifetime |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Reachable Time |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Retrans Timer |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Options ...
+ +-+-+-+-+-+-+-+-+-+-+-+-
+ */
+ final byte DEFAULT_HOPLIMIT = 64;
+ ra.put(ICMPV6_ND_ROUTER_ADVERT)
+ .put(asByte(0))
+ .putShort(asShort(0))
+ .put(DEFAULT_HOPLIMIT)
+ // RFC 4191 "high" preference, iff. advertising a default route.
+ .put(hasDefaultRoute ? asByte(0x08) : asByte(0))
+ .putShort(hasDefaultRoute ? asShort(DEFAULT_LIFETIME) : asShort(0))
+ .putInt(0)
+ .putInt(0);
+ }
+
+ private static void putSlla(ByteBuffer ra, byte[] slla) {
+ /**
+ Source/Target Link-layer Address
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Type | Length | Link-Layer Address ...
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ if (slla == null || slla.length != 6) {
+ // Only IEEE 802.3 6-byte addresses are supported.
+ return;
+ }
+ final byte ND_OPTION_SLLA = 1;
+ final byte SLLA_NUM_8OCTETS = 1;
+ ra.put(ND_OPTION_SLLA)
+ .put(SLLA_NUM_8OCTETS)
+ .put(slla);
+ }
+
+ private static void putExpandedFlagsOption(ByteBuffer ra) {
+ /**
+ Router Advertisement Expanded Flags Option
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Type | Length | Bit fields available ..
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ... for assignment |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ final byte ND_OPTION_EFO = 26;
+ final byte EFO_NUM_8OCTETS = 1;
+
+ ra.put(ND_OPTION_EFO)
+ .put(EFO_NUM_8OCTETS)
+ .putShort(asShort(0))
+ .putInt(0);
+ }
+
+ private static void putMtu(ByteBuffer ra, int mtu) {
+ /**
+ MTU
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Type | Length | Reserved |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | MTU |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ final byte ND_OPTION_MTU = 5;
+ final byte MTU_NUM_8OCTETS = 1;
+ ra.put(ND_OPTION_MTU)
+ .put(MTU_NUM_8OCTETS)
+ .putShort(asShort(0))
+ .putInt((mtu < IPV6_MIN_MTU) ? IPV6_MIN_MTU : mtu);
+ }
+
+ private static void putPio(ByteBuffer ra, IpPrefix ipp,
+ int validTime, int preferredTime) {
+ /**
+ Prefix Information
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Type | Length | Prefix Length |L|A| Reserved1 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Valid Lifetime |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Preferred Lifetime |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Reserved2 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ + +
+ | |
+ + Prefix +
+ | |
+ + +
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ final int prefixLength = ipp.getPrefixLength();
+ if (prefixLength != 64) {
+ return;
+ }
+ final byte ND_OPTION_PIO = 3;
+ final byte PIO_NUM_8OCTETS = 4;
+
+ if (validTime < 0) validTime = 0;
+ if (preferredTime < 0) preferredTime = 0;
+ if (preferredTime > validTime) preferredTime = validTime;
+
+ final byte[] addr = ipp.getAddress().getAddress();
+ ra.put(ND_OPTION_PIO)
+ .put(PIO_NUM_8OCTETS)
+ .put(asByte(prefixLength))
+ .put(asByte(0xc0)) /* L & A set */
+ .putInt(validTime)
+ .putInt(preferredTime)
+ .putInt(0)
+ .put(addr);
+ }
+
+ private static void putRio(ByteBuffer ra, IpPrefix ipp) {
+ /**
+ Route Information Option
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Type | Length | Prefix Length |Resvd|Prf|Resvd|
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Route Lifetime |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Prefix (Variable Length) |
+ . .
+ . .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ final int prefixLength = ipp.getPrefixLength();
+ if (prefixLength > 64) {
+ return;
+ }
+ final byte ND_OPTION_RIO = 24;
+ final byte RIO_NUM_8OCTETS = asByte(
+ (prefixLength == 0) ? 1 : (prefixLength <= 8) ? 2 : 3);
+
+ final byte[] addr = ipp.getAddress().getAddress();
+ ra.put(ND_OPTION_RIO)
+ .put(RIO_NUM_8OCTETS)
+ .put(asByte(prefixLength))
+ .put(asByte(0x18))
+ .putInt(DEFAULT_LIFETIME);
+
+ // Rely upon an IpPrefix's address being properly zeroed.
+ if (prefixLength > 0) {
+ ra.put(addr, 0, (prefixLength <= 64) ? 8 : 16);
+ }
+ }
+
+ private static void putRdnss(ByteBuffer ra, Set<Inet6Address> dnses, int lifetime) {
+ /**
+ Recursive DNS Server (RDNSS) Option
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Type | Length | Reserved |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Lifetime |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ : Addresses of IPv6 Recursive DNS Servers :
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ final byte ND_OPTION_RDNSS = 25;
+ final byte RDNSS_NUM_8OCTETS = asByte(dnses.size() * 2 + 1);
+ ra.put(ND_OPTION_RDNSS)
+ .put(RDNSS_NUM_8OCTETS)
+ .putShort(asShort(0))
+ .putInt(lifetime);
+
+ for (Inet6Address dns : dnses) {
+ // NOTE: If the full of list DNS servers doesn't fit in the packet,
+ // this code will cause a buffer overflow and the RA won't include
+ // this instance of the option at all.
+ //
+ // TODO: Consider looking at ra.remaining() to determine how many
+ // DNS servers will fit, and adding only those.
+ ra.put(dns.getAddress());
+ }
+ }
+
+ private boolean createSocket() {
+ final int SEND_TIMEOUT_MS = 300;
+
+ try {
+ mSocket = Os.socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
+ // Setting SNDTIMEO is purely for defensive purposes.
+ Os.setsockoptTimeval(
+ mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(SEND_TIMEOUT_MS));
+ Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mIfName);
+ NetworkUtils.protectFromVpn(mSocket);
+ NetworkUtils.setupRaSocket(mSocket, mIfIndex);
+ } catch (ErrnoException | IOException e) {
+ Log.e(TAG, "Failed to create RA daemon socket: " + e);
+ return false;
+ }
+
+ return true;
+ }
+
+ private void closeSocket() {
+ if (mSocket != null) {
+ try {
+ IoBridge.closeAndSignalBlockedThreads(mSocket);
+ } catch (IOException ignored) {}
+ }
+ mSocket = null;
+ }
+
+ private boolean isSocketValid() {
+ final FileDescriptor s = mSocket;
+ return (s != null) && s.valid();
+ }
+
+ private boolean isSuitableDestination(InetSocketAddress dest) {
+ if (mAllNodes.equals(dest)) {
+ return true;
+ }
+
+ final InetAddress destip = dest.getAddress();
+ return (destip instanceof Inet6Address) &&
+ destip.isLinkLocalAddress() &&
+ (((Inet6Address) destip).getScopeId() == mIfIndex);
+ }
+
+ private void maybeSendRA(InetSocketAddress dest) {
+ if (dest == null || !isSuitableDestination(dest)) {
+ dest = mAllNodes;
+ }
+
+ try {
+ synchronized (mLock) {
+ if (mRaLength < MIN_RA_HEADER_SIZE) {
+ // No actual RA to send.
+ return;
+ }
+ Os.sendto(mSocket, mRA, 0, mRaLength, 0, dest);
+ }
+ Log.d(TAG, "RA sendto " + dest.getAddress().getHostAddress());
+ } catch (ErrnoException | SocketException e) {
+ if (isSocketValid()) {
+ Log.e(TAG, "sendto error: " + e);
+ }
+ }
+ }
+
+ private final class UnicastResponder extends Thread {
+ private final InetSocketAddress solicitor = new InetSocketAddress();
+ // The recycled buffer for receiving Router Solicitations from clients.
+ // If the RS is larger than IPV6_MIN_MTU the packets are truncated.
+ // This is fine since currently only byte 0 is examined anyway.
+ private final byte mSolication[] = new byte[IPV6_MIN_MTU];
+
+ @Override
+ public void run() {
+ while (isSocketValid()) {
+ try {
+ // Blocking receive.
+ final int rval = Os.recvfrom(
+ mSocket, mSolication, 0, mSolication.length, 0, solicitor);
+ // Do the least possible amount of validation.
+ if (rval < 1 || mSolication[0] != ICMPV6_ND_ROUTER_SOLICIT) {
+ continue;
+ }
+ } catch (ErrnoException | SocketException e) {
+ if (isSocketValid()) {
+ Log.e(TAG, "recvfrom error: " + e);
+ }
+ continue;
+ }
+
+ maybeSendRA(solicitor);
+ }
+ }
+ }
+
+ // TODO: Consider moving this to run on a provided Looper as a Handler,
+ // with WakeupMessage-style messages providing the timer driven input.
+ private final class MulticastTransmitter extends Thread {
+ private final Random mRandom = new Random();
+ private final AtomicInteger mUrgentAnnouncements = new AtomicInteger(0);
+
+ @Override
+ public void run() {
+ while (isSocketValid()) {
+ try {
+ Thread.sleep(getNextMulticastTransmitDelayMs());
+ } catch (InterruptedException ignored) {
+ // Stop sleeping, immediately send an RA, and continue.
+ }
+
+ maybeSendRA(mAllNodes);
+ synchronized (mLock) {
+ if (mDeprecatedInfoTracker.decrementCounters()) {
+ // At least one deprecated PIO has been removed;
+ // reassemble the RA.
+ assembleRaLocked();
+ }
+ }
+ }
+ }
+
+ public void hup() {
+ // Set to one fewer that the desired number, because as soon as
+ // the thread interrupt is processed we immediately send an RA
+ // and mUrgentAnnouncements is not examined until the subsequent
+ // sleep interval computation (i.e. this way we send 3 and not 4).
+ mUrgentAnnouncements.set(MAX_URGENT_RTR_ADVERTISEMENTS - 1);
+ interrupt();
+ }
+
+ private int getNextMulticastTransmitDelaySec() {
+ boolean deprecationInProgress = false;
+ synchronized (mLock) {
+ if (mRaLength < MIN_RA_HEADER_SIZE) {
+ // No actual RA to send; just sleep for 1 day.
+ return DAY_IN_SECONDS;
+ }
+ deprecationInProgress = !mDeprecatedInfoTracker.isEmpty();
+ }
+
+ final int urgentPending = mUrgentAnnouncements.getAndDecrement();
+ if ((urgentPending > 0) || deprecationInProgress) {
+ return MIN_DELAY_BETWEEN_RAS_SEC;
+ }
+
+ return MIN_RTR_ADV_INTERVAL_SEC + mRandom.nextInt(
+ MAX_RTR_ADV_INTERVAL_SEC - MIN_RTR_ADV_INTERVAL_SEC);
+ }
+
+ private long getNextMulticastTransmitDelayMs() {
+ return 1000 * (long) getNextMulticastTransmitDelaySec();
+ }
+ }
+}
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 7a3ebf4..05301c1 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -764,9 +764,8 @@
public void updateIfNeededLocked() {
throwIfDestroyedLocked();
- if (readConfigurationLocked()) {
- onConfigurationChangedLocked();
- }
+ readConfigurationLocked();
+ onConfigurationChangedLocked();
}
public void destroyLocked() {
@@ -841,14 +840,12 @@
pw.println();
}
- private boolean readConfigurationLocked() {
- boolean somethingChanged = false;
- somethingChanged |= readInstalledPrintServicesLocked();
- somethingChanged |= readDisabledPrintServicesLocked();
- return somethingChanged;
+ private void readConfigurationLocked() {
+ readInstalledPrintServicesLocked();
+ readDisabledPrintServicesLocked();
}
- private boolean readInstalledPrintServicesLocked() {
+ private void readInstalledPrintServicesLocked() {
Set<PrintServiceInfo> tempPrintServices = new HashSet<PrintServiceInfo>();
List<ResolveInfo> installedServices = mContext.getPackageManager()
@@ -872,39 +869,8 @@
tempPrintServices.add(PrintServiceInfo.create(installedService, mContext));
}
- boolean someServiceChanged = false;
-
- if (tempPrintServices.size() != mInstalledServices.size()) {
- someServiceChanged = true;
- } else {
- for (PrintServiceInfo newService: tempPrintServices) {
- final int oldServiceIndex = mInstalledServices.indexOf(newService);
- if (oldServiceIndex < 0) {
- someServiceChanged = true;
- break;
- }
- // PrintServiceInfo#equals compares only the id not all members,
- // so we are also comparing the members coming from meta-data.
- PrintServiceInfo oldService = mInstalledServices.get(oldServiceIndex);
- if (!TextUtils.equals(oldService.getAddPrintersActivityName(),
- newService.getAddPrintersActivityName())
- || !TextUtils.equals(oldService.getAdvancedOptionsActivityName(),
- newService.getAdvancedOptionsActivityName())
- || !TextUtils.equals(oldService.getSettingsActivityName(),
- newService.getSettingsActivityName())) {
- someServiceChanged = true;
- break;
- }
- }
- }
-
- if (someServiceChanged) {
- mInstalledServices.clear();
- mInstalledServices.addAll(tempPrintServices);
- return true;
- }
-
- return false;
+ mInstalledServices.clear();
+ mInstalledServices.addAll(tempPrintServices);
}
/**
@@ -944,16 +910,14 @@
*
* @return true if the state changed.
*/
- private boolean readDisabledPrintServicesLocked() {
+ private void readDisabledPrintServicesLocked() {
Set<ComponentName> tempDisabledServiceNameSet = new HashSet<ComponentName>();
readPrintServicesFromSettingLocked(Settings.Secure.DISABLED_PRINT_SERVICES,
tempDisabledServiceNameSet);
if (!tempDisabledServiceNameSet.equals(mDisabledServices)) {
mDisabledServices.clear();
mDisabledServices.addAll(tempDisabledServiceNameSet);
- return true;
}
- return false;
}
private void readPrintServicesFromSettingLocked(String setting,
diff --git a/services/retaildemo/Android.mk b/services/retaildemo/Android.mk
new file mode 100644
index 0000000..670c6bf
--- /dev/null
+++ b/services/retaildemo/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := services.retaildemo
+
+LOCAL_SRC_FILES += \
+ $(call all-java-files-under,java)
+
+LOCAL_JAVA_LIBRARIES := services.core
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java b/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java
new file mode 100644
index 0000000..2038c9e
--- /dev/null
+++ b/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.retaildemo;
+
+import android.app.AppGlobals;
+import android.app.PackageInstallObserver;
+import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Helper class for installing preloaded APKs
+ */
+class PreloadAppsInstaller {
+ private static final String SYSTEM_SERVER_PACKAGE_NAME = "android";
+ private static String TAG = PreloadAppsInstaller.class.getSimpleName();
+ private static final String PRELOAD_APK_EXT = ".apk.preload";
+ private static boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final IPackageManager mPackageManager;
+ private final File preloadsAppsDirectory;
+ private final Context mContext;
+
+ private final Map<String, String> mApkToPackageMap;
+
+ PreloadAppsInstaller(Context context) {
+ this(context, AppGlobals.getPackageManager(), Environment.getDataPreloadsAppsDirectory());
+ }
+
+ @VisibleForTesting
+ PreloadAppsInstaller(Context context, IPackageManager packageManager, File preloadsAppsDirectory) {
+ mContext = context;
+ mPackageManager = packageManager;
+ mApkToPackageMap = Collections.synchronizedMap(new ArrayMap<>());
+ this.preloadsAppsDirectory = preloadsAppsDirectory;
+ }
+
+ void installApps(int userId) {
+ File[] files = preloadsAppsDirectory.listFiles();
+ AppInstallCounter counter = new AppInstallCounter(mContext, userId);
+ if (ArrayUtils.isEmpty(files)) {
+ counter.setExpectedAppsCount(0);
+ return;
+ }
+ int expectedCount = 0;
+ for (File file : files) {
+ String apkName = file.getName();
+ if (apkName.endsWith(PRELOAD_APK_EXT) && file.isFile()) {
+ String packageName = mApkToPackageMap.get(apkName);
+ if (packageName != null) {
+ try {
+ expectedCount++;
+ installExistingPackage(packageName, userId, counter);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to install existing package " + packageName, e);
+ }
+ } else {
+ try {
+ installPackage(file, userId, counter);
+ expectedCount++;
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to install package from " + file, e);
+ }
+ }
+ }
+ }
+ counter.setExpectedAppsCount(expectedCount);
+ }
+
+ private void installExistingPackage(String packageName, int userId,
+ AppInstallCounter counter) {
+ if (DEBUG) {
+ Log.d(TAG, "installExistingPackage " + packageName + " u" + userId);
+ }
+ try {
+ mPackageManager.installExistingPackageAsUser(packageName, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } finally {
+ counter.appInstallFinished();
+ }
+ }
+
+ private void installPackage(File file, final int userId, AppInstallCounter counter)
+ throws IOException, RemoteException {
+ final String apkName = file.getName();
+ if (DEBUG) {
+ Log.d(TAG, "installPackage " + apkName + " u" + userId);
+ }
+ mPackageManager.installPackageAsUser(file.getPath(), new PackageInstallObserver() {
+ @Override
+ public void onPackageInstalled(String basePackageName, int returnCode, String msg,
+ Bundle extras) {
+ if (DEBUG) {
+ Log.d(TAG, "Package " + basePackageName + " installed u" + userId
+ + " returnCode: " + returnCode + " msg: " + msg);
+ }
+ // Don't notify the counter for now, we'll do it in installExistingPackage
+ if (returnCode == PackageManager.INSTALL_SUCCEEDED) {
+ mApkToPackageMap.put(apkName, basePackageName);
+ // Install on user 0 so that the package is cached when demo user is re-created
+ installExistingPackage(basePackageName, UserHandle.USER_SYSTEM, counter);
+ } else if (returnCode == PackageManager.INSTALL_FAILED_ALREADY_EXISTS) {
+ // This can only happen in first session after a reboot
+ if (!mApkToPackageMap.containsKey(apkName)) {
+ mApkToPackageMap.put(apkName, basePackageName);
+ }
+ installExistingPackage(basePackageName, userId, counter);
+ } else {
+ Log.e(TAG, "Package " + basePackageName + " cannot be installed from "
+ + apkName + ": " + msg + " (returnCode " + returnCode + ")");
+ counter.appInstallFinished();
+ }
+ }
+ }.getBinder(), 0, SYSTEM_SERVER_PACKAGE_NAME, userId);
+ }
+
+ private static class AppInstallCounter {
+ private int expectedCount = -1; // -1 means expectedCount not set
+ private int finishedCount;
+ private final Context mContext;
+ private final int userId;
+
+ AppInstallCounter(Context context, int userId) {
+ mContext = context;
+ this.userId = userId;
+ }
+
+ synchronized void appInstallFinished() {
+ this.finishedCount++;
+ checkIfAllFinished();
+ }
+
+ synchronized void setExpectedAppsCount(int expectedCount) {
+ this.expectedCount = expectedCount;
+ checkIfAllFinished();
+ }
+
+ private void checkIfAllFinished() {
+ if (expectedCount == finishedCount) {
+ Log.i(TAG, "All preloads finished installing for user " + userId);
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.DEMO_USER_SETUP_COMPLETE, "1", userId);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
new file mode 100644
index 0000000..6b273210
--- /dev/null
+++ b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.retaildemo;
+
+import android.Manifest;
+import android.app.ActivityManagerInternal;
+import android.app.ActivityManagerNative;
+import android.app.AppGlobals;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.RetailDemoModeServiceInternal;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.content.res.Configuration;
+import android.database.ContentObserver;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraManager;
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.CallLog;
+import android.provider.MediaStore;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
+import android.util.Slog;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
+import com.android.server.SystemService;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.retaildemo.UserInactivityCountdownDialog.OnCountDownExpiredListener;
+
+import java.io.File;
+import java.util.ArrayList;
+
+public class RetailDemoModeService extends SystemService {
+ private static final boolean DEBUG = false;
+
+ private static final String TAG = RetailDemoModeService.class.getSimpleName();
+ private static final String DEMO_USER_NAME = "Demo";
+ private static final String ACTION_RESET_DEMO =
+ "com.android.server.retaildemo.ACTION_RESET_DEMO";
+ private static final String SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED = "sys.retaildemo.enabled";
+
+ private static final int MSG_TURN_SCREEN_ON = 0;
+ private static final int MSG_INACTIVITY_TIME_OUT = 1;
+ private static final int MSG_START_NEW_SESSION = 2;
+
+ private static final long SCREEN_WAKEUP_DELAY = 2500;
+ private static final long USER_INACTIVITY_TIMEOUT_MIN = 10000;
+ private static final long USER_INACTIVITY_TIMEOUT_DEFAULT = 90000;
+ private static final long WARNING_DIALOG_TIMEOUT_DEFAULT = 0;
+ private static final long MILLIS_PER_SECOND = 1000;
+
+ private static final int[] VOLUME_STREAMS_TO_MUTE = {
+ AudioSystem.STREAM_RING,
+ AudioSystem.STREAM_MUSIC
+ };
+
+ // Tron Vars
+ private static final String DEMO_SESSION_COUNT = "retail_demo_session_count";
+ private static final String DEMO_SESSION_DURATION = "retail_demo_session_duration";
+
+ boolean mDeviceInDemoMode = false;
+ int mCurrentUserId = UserHandle.USER_SYSTEM;
+ long mUserInactivityTimeout;
+ long mWarningDialogTimeout;
+ private ActivityManagerService mAms;
+ private ActivityManagerInternal mAmi;
+ private AudioManager mAudioManager;
+ private NotificationManager mNm;
+ private UserManager mUm;
+ private PowerManager mPm;
+ private PowerManager.WakeLock mWakeLock;
+ Handler mHandler;
+ private ServiceThread mHandlerThread;
+ private PendingIntent mResetDemoPendingIntent;
+ private CameraManager mCameraManager;
+ private String[] mCameraIdsWithFlash;
+ private Configuration mSystemUserConfiguration;
+ private PreloadAppsInstaller mPreloadAppsInstaller;
+
+ final Object mActivityLock = new Object();
+ // Whether the newly created demo user has interacted with the screen yet
+ @GuardedBy("mActivityLock")
+ boolean mUserUntouched;
+ @GuardedBy("mActivityLock")
+ long mFirstUserActivityTime;
+ @GuardedBy("mActivityLock")
+ long mLastUserActivityTime;
+
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!mDeviceInDemoMode) {
+ return;
+ }
+ switch (intent.getAction()) {
+ case Intent.ACTION_SCREEN_OFF:
+ mHandler.removeMessages(MSG_TURN_SCREEN_ON);
+ mHandler.sendEmptyMessageDelayed(MSG_TURN_SCREEN_ON, SCREEN_WAKEUP_DELAY);
+ break;
+ case ACTION_RESET_DEMO:
+ mHandler.sendEmptyMessage(MSG_START_NEW_SESSION);
+ break;
+ }
+ }
+ };
+
+ final class MainHandler extends Handler {
+
+ MainHandler(Looper looper) {
+ super(looper, null, true);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_TURN_SCREEN_ON:
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ mWakeLock.acquire();
+ break;
+ case MSG_INACTIVITY_TIME_OUT:
+ if (isDemoLauncherDisabled()) {
+ Slog.i(TAG, "User inactivity timeout reached");
+ showInactivityCountdownDialog();
+ }
+ break;
+ case MSG_START_NEW_SESSION:
+ if (DEBUG) {
+ Slog.d(TAG, "Switching to a new demo user");
+ }
+ removeMessages(MSG_START_NEW_SESSION);
+ removeMessages(MSG_INACTIVITY_TIME_OUT);
+ if (mCurrentUserId != UserHandle.USER_SYSTEM) {
+ logSessionDuration();
+ }
+ final UserInfo demoUser = getUserManager().createUser(DEMO_USER_NAME,
+ UserInfo.FLAG_DEMO | UserInfo.FLAG_EPHEMERAL);
+ if (demoUser != null) {
+ setupDemoUser(demoUser);
+ getActivityManager().switchUser(demoUser.id);
+ }
+ break;
+ }
+ }
+ }
+
+ private class SettingsObserver extends ContentObserver {
+
+ private final static String KEY_USER_INACTIVITY_TIMEOUT = "user_inactivity_timeout_ms";
+ private final static String KEY_WARNING_DIALOG_TIMEOUT = "warning_dialog_timeout_ms";
+
+ private final Uri mDeviceDemoModeUri = Settings.Global
+ .getUriFor(Settings.Global.DEVICE_DEMO_MODE);
+ private final Uri mDeviceProvisionedUri = Settings.Global
+ .getUriFor(Settings.Global.DEVICE_PROVISIONED);
+ private final Uri mRetailDemoConstantsUri = Settings.Global
+ .getUriFor(Settings.Global.RETAIL_DEMO_MODE_CONSTANTS);
+
+ private final KeyValueListParser mParser = new KeyValueListParser(',');
+
+ public SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void register() {
+ ContentResolver cr = getContext().getContentResolver();
+ cr.registerContentObserver(mDeviceDemoModeUri, false, this, UserHandle.USER_SYSTEM);
+ cr.registerContentObserver(mDeviceProvisionedUri, false, this, UserHandle.USER_SYSTEM);
+ cr.registerContentObserver(mRetailDemoConstantsUri, false, this,
+ UserHandle.USER_SYSTEM);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (mRetailDemoConstantsUri.equals(uri)) {
+ refreshTimeoutConstants();
+ return;
+ }
+ if (mDeviceDemoModeUri.equals(uri)) {
+ mDeviceInDemoMode = UserManager.isDeviceInDemoMode(getContext());
+ if (mDeviceInDemoMode) {
+ putDeviceInDemoMode();
+ } else {
+ SystemProperties.set(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED, "0");
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ }
+ }
+ // If device is provisioned and left demo mode - run the cleanup in demo folder
+ if (!mDeviceInDemoMode && isDeviceProvisioned()) {
+ // Run on the bg thread to not block the fg thread
+ BackgroundThread.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ if (!deletePreloadsFolderContents()) {
+ Slog.w(TAG, "Failed to delete preloads folder contents");
+ }
+ }
+ });
+ }
+ }
+
+ private void refreshTimeoutConstants() {
+ try {
+ mParser.setString(Settings.Global.getString(getContext().getContentResolver(),
+ Settings.Global.RETAIL_DEMO_MODE_CONSTANTS));
+ } catch (IllegalArgumentException exc) {
+ Slog.e(TAG, "Invalid string passed to KeyValueListParser");
+ // Consuming the exception to fall back to default values.
+ }
+ mWarningDialogTimeout = mParser.getLong(KEY_WARNING_DIALOG_TIMEOUT,
+ WARNING_DIALOG_TIMEOUT_DEFAULT);
+ mUserInactivityTimeout = mParser.getLong(KEY_USER_INACTIVITY_TIMEOUT,
+ USER_INACTIVITY_TIMEOUT_DEFAULT);
+ mUserInactivityTimeout = Math.max(mUserInactivityTimeout, USER_INACTIVITY_TIMEOUT_MIN);
+ }
+ }
+
+ private void showInactivityCountdownDialog() {
+ UserInactivityCountdownDialog dialog = new UserInactivityCountdownDialog(getContext(),
+ mWarningDialogTimeout, MILLIS_PER_SECOND);
+ dialog.setNegativeButtonClickListener(null);
+ dialog.setPositiveButtonClickListener(new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mHandler.sendEmptyMessage(MSG_START_NEW_SESSION);
+ }
+ });
+ dialog.setOnCountDownExpiredListener(new OnCountDownExpiredListener() {
+ @Override
+ public void onCountDownExpired() {
+ mHandler.sendEmptyMessage(MSG_START_NEW_SESSION);
+ }
+ });
+ dialog.show();
+ }
+
+ public RetailDemoModeService(Context context) {
+ super(context);
+ synchronized (mActivityLock) {
+ mFirstUserActivityTime = mLastUserActivityTime = SystemClock.uptimeMillis();
+ }
+ }
+
+ private Notification createResetNotification() {
+ return new Notification.Builder(getContext())
+ .setContentTitle(getContext().getString(R.string.reset_retail_demo_mode_title))
+ .setContentText(getContext().getString(R.string.reset_retail_demo_mode_text))
+ .setOngoing(true)
+ .setSmallIcon(R.drawable.platlogo)
+ .setShowWhen(false)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setContentIntent(getResetDemoPendingIntent())
+ .setColor(getContext().getColor(R.color.system_notification_accent_color))
+ .build();
+ }
+
+ private PendingIntent getResetDemoPendingIntent() {
+ if (mResetDemoPendingIntent == null) {
+ Intent intent = new Intent(ACTION_RESET_DEMO);
+ mResetDemoPendingIntent = PendingIntent.getBroadcast(getContext(), 0, intent, 0);
+ }
+ return mResetDemoPendingIntent;
+ }
+
+ boolean isDemoLauncherDisabled() {
+ IPackageManager pm = AppGlobals.getPackageManager();
+ int enabledState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+ String demoLauncherComponent = getContext().getResources()
+ .getString(R.string.config_demoModeLauncherComponent);
+ try {
+ enabledState = pm.getComponentEnabledSetting(
+ ComponentName.unflattenFromString(demoLauncherComponent),
+ mCurrentUserId);
+ } catch (RemoteException exc) {
+ Slog.e(TAG, "Unable to talk to Package Manager", exc);
+ }
+ return enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+ }
+
+ private void setupDemoUser(UserInfo userInfo) {
+ UserManager um = getUserManager();
+ UserHandle user = UserHandle.of(userInfo.id);
+ um.setUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, true, user);
+ um.setUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true, user);
+ um.setUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, true, user);
+ um.setUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER, true, user);
+ um.setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user);
+ um.setUserRestriction(UserManager.DISALLOW_CONFIG_BLUETOOTH, true, user);
+ // Set this to false because the default is true on user creation
+ um.setUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, false, user);
+ // Disallow rebooting in safe mode - controlled by user 0
+ getUserManager().setUserRestriction(UserManager.DISALLOW_SAFE_BOOT, true,
+ UserHandle.SYSTEM);
+ Settings.Secure.putIntForUser(getContext().getContentResolver(),
+ Settings.Secure.SKIP_FIRST_USE_HINTS, 1, userInfo.id);
+ Settings.Global.putInt(getContext().getContentResolver(),
+ Settings.Global.PACKAGE_VERIFIER_ENABLE, 0);
+ grantRuntimePermissionToCamera(user);
+ clearPrimaryCallLog();
+ }
+
+ private void grantRuntimePermissionToCamera(UserHandle user) {
+ final Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+ final PackageManager pm = getContext().getPackageManager();
+ final ResolveInfo handler = pm.resolveActivityAsUser(cameraIntent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ user.getIdentifier());
+ if (handler == null || handler.activityInfo == null) {
+ return;
+ }
+ try {
+ pm.grantRuntimePermission(handler.activityInfo.packageName,
+ Manifest.permission.ACCESS_FINE_LOCATION, user);
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+
+ private void clearPrimaryCallLog() {
+ final ContentResolver resolver = getContext().getContentResolver();
+
+ // Deleting primary user call log so that it doesn't get copied to the new demo user
+ final Uri uri = CallLog.Calls.CONTENT_URI;
+ try {
+ resolver.delete(uri, null, null);
+ } catch (Exception e) {
+ Slog.w(TAG, "Deleting call log failed: " + e);
+ }
+ }
+
+ void logSessionDuration() {
+ final int sessionDuration;
+ synchronized (mActivityLock) {
+ sessionDuration = (int) ((mLastUserActivityTime - mFirstUserActivityTime) / 1000);
+ }
+ MetricsLogger.histogram(getContext(), DEMO_SESSION_DURATION, sessionDuration);
+ }
+
+ private ActivityManagerService getActivityManager() {
+ if (mAms == null) {
+ mAms = (ActivityManagerService) ActivityManagerNative.getDefault();
+ }
+ return mAms;
+ }
+
+ private UserManager getUserManager() {
+ if (mUm == null) {
+ mUm = getContext().getSystemService(UserManager.class);
+ }
+ return mUm;
+ }
+
+ private AudioManager getAudioManager() {
+ if (mAudioManager == null) {
+ mAudioManager = getContext().getSystemService(AudioManager.class);
+ }
+ return mAudioManager;
+ }
+
+ private boolean isDeviceProvisioned() {
+ return Settings.Global.getInt(
+ getContext().getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+ }
+
+ private boolean deletePreloadsFolderContents() {
+ final File dir = Environment.getDataPreloadsDirectory();
+ Slog.i(TAG, "Deleting contents of " + dir);
+ return FileUtils.deleteContents(dir);
+ }
+
+ private void registerBroadcastReceiver() {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(ACTION_RESET_DEMO);
+ getContext().registerReceiver(mBroadcastReceiver, filter);
+ }
+
+ private String[] getCameraIdsWithFlash() {
+ ArrayList<String> cameraIdsList = new ArrayList<String>();
+ try {
+ for (String cameraId : mCameraManager.getCameraIdList()) {
+ CameraCharacteristics c = mCameraManager.getCameraCharacteristics(cameraId);
+ if (Boolean.TRUE.equals(c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE))) {
+ cameraIdsList.add(cameraId);
+ }
+ }
+ } catch (CameraAccessException e) {
+ Slog.e(TAG, "Unable to access camera while getting camera id list", e);
+ }
+ return cameraIdsList.toArray(new String[cameraIdsList.size()]);
+ }
+
+ private void turnOffAllFlashLights() {
+ for (String cameraId : mCameraIdsWithFlash) {
+ try {
+ mCameraManager.setTorchMode(cameraId, false);
+ } catch (CameraAccessException e) {
+ Slog.e(TAG, "Unable to access camera " + cameraId + " while turning off flash", e);
+ }
+ }
+ }
+
+ private void muteVolumeStreams() {
+ for (int stream : VOLUME_STREAMS_TO_MUTE) {
+ getAudioManager().setStreamVolume(stream, getAudioManager().getStreamMinVolume(stream),
+ 0);
+ }
+ }
+
+ private Configuration getSystemUsersConfiguration() {
+ if (mSystemUserConfiguration == null) {
+ Settings.System.getConfiguration(getContext().getContentResolver(),
+ mSystemUserConfiguration = new Configuration());
+ }
+ return mSystemUserConfiguration;
+ }
+
+ private void putDeviceInDemoMode() {
+ SystemProperties.set(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED, "1");
+ mHandler.sendEmptyMessage(MSG_START_NEW_SESSION);
+ }
+
+ @Override
+ public void onStart() {
+ if (DEBUG) {
+ Slog.d(TAG, "Service starting up");
+ }
+ mHandlerThread = new ServiceThread(TAG, android.os.Process.THREAD_PRIORITY_FOREGROUND,
+ false);
+ mHandlerThread.start();
+ mHandler = new MainHandler(mHandlerThread.getLooper());
+ publishLocalService(RetailDemoModeServiceInternal.class, mLocalService);
+ }
+
+ @Override
+ public void onBootPhase(int bootPhase) {
+ switch (bootPhase) {
+ case PHASE_THIRD_PARTY_APPS_CAN_START:
+ mPreloadAppsInstaller = new PreloadAppsInstaller(getContext());
+ mPm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
+ mAmi = LocalServices.getService(ActivityManagerInternal.class);
+ mWakeLock = mPm
+ .newWakeLock(
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP,
+ TAG);
+ mNm = NotificationManager.from(getContext());
+ mCameraManager = (CameraManager) getContext()
+ .getSystemService(Context.CAMERA_SERVICE);
+ mCameraIdsWithFlash = getCameraIdsWithFlash();
+ SettingsObserver settingsObserver = new SettingsObserver(mHandler);
+ settingsObserver.register();
+ settingsObserver.refreshTimeoutConstants();
+ registerBroadcastReceiver();
+ break;
+ case PHASE_BOOT_COMPLETED:
+ if (UserManager.isDeviceInDemoMode(getContext())) {
+ mDeviceInDemoMode = true;
+ putDeviceInDemoMode();
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void onSwitchUser(int userId) {
+ if (!mDeviceInDemoMode) {
+ return;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "onSwitchUser: " + userId);
+ }
+ final UserInfo ui = getUserManager().getUserInfo(userId);
+ if (!ui.isDemo()) {
+ Slog.wtf(TAG, "Should not allow switch to non-demo user in demo mode");
+ return;
+ }
+ if (!mWakeLock.isHeld()) {
+ mWakeLock.acquire();
+ }
+ mCurrentUserId = userId;
+ mAmi.updatePersistentConfigurationForUser(getSystemUsersConfiguration(), userId);
+ turnOffAllFlashLights();
+ muteVolumeStreams();
+ // Disable lock screen for demo users.
+ LockPatternUtils lockPatternUtils = new LockPatternUtils(getContext());
+ lockPatternUtils.setLockScreenDisabled(true, userId);
+ mNm.notifyAsUser(TAG, 1, createResetNotification(), UserHandle.of(userId));
+
+ synchronized (mActivityLock) {
+ mUserUntouched = true;
+ }
+ MetricsLogger.count(getContext(), DEMO_SESSION_COUNT, 1);
+ mHandler.removeMessages(MSG_INACTIVITY_TIME_OUT);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mPreloadAppsInstaller.installApps(userId);
+ }
+ });
+ }
+
+ private RetailDemoModeServiceInternal mLocalService = new RetailDemoModeServiceInternal() {
+ private static final long USER_ACTIVITY_DEBOUNCE_TIME = 2000;
+
+ @Override
+ public void onUserActivity() {
+ if (!mDeviceInDemoMode) {
+ return;
+ }
+ long timeOfActivity = SystemClock.uptimeMillis();
+ synchronized (mActivityLock) {
+ if (timeOfActivity < mLastUserActivityTime + USER_ACTIVITY_DEBOUNCE_TIME) {
+ return;
+ }
+ mLastUserActivityTime = timeOfActivity;
+ if (mUserUntouched && isDemoLauncherDisabled()) {
+ Slog.d(TAG, "retail_demo first touch");
+ mUserUntouched = false;
+ mFirstUserActivityTime = timeOfActivity;
+ }
+ }
+ mHandler.removeMessages(MSG_INACTIVITY_TIME_OUT);
+ mHandler.sendEmptyMessageDelayed(MSG_INACTIVITY_TIME_OUT, mUserInactivityTimeout);
+ }
+ };
+}
diff --git a/services/retaildemo/java/com/android/server/retaildemo/UserInactivityCountdownDialog.java b/services/retaildemo/java/com/android/server/retaildemo/UserInactivityCountdownDialog.java
new file mode 100644
index 0000000..d14f4eb
--- /dev/null
+++ b/services/retaildemo/java/com/android/server/retaildemo/UserInactivityCountdownDialog.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.retaildemo;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.os.CountDownTimer;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+public class UserInactivityCountdownDialog extends AlertDialog {
+
+ private OnCountDownExpiredListener mOnCountDownExpiredListener;
+ private CountDownTimer mCountDownTimer;
+ private long mCountDownDuration;
+ private long mRefreshInterval;
+
+ UserInactivityCountdownDialog(Context context, long duration, long refreshInterval) {
+ super(context);
+ mCountDownDuration = duration;
+ mRefreshInterval = refreshInterval;
+
+ getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+ WindowManager.LayoutParams attrs = getWindow().getAttributes();
+ attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ getWindow().setAttributes(attrs);
+
+ setTitle(R.string.demo_user_inactivity_timeout_title);
+ setMessage(getContext().getString(R.string.demo_user_inactivity_timeout_countdown,
+ duration));
+ }
+
+ public void setOnCountDownExpiredListener(
+ OnCountDownExpiredListener onCountDownExpiredListener) {
+ mOnCountDownExpiredListener = onCountDownExpiredListener;
+ }
+
+ public void setPositiveButtonClickListener(OnClickListener onClickListener) {
+ setButton(Dialog.BUTTON_POSITIVE,
+ getContext().getString(R.string.demo_user_inactivity_timeout_right_button),
+ onClickListener);
+ }
+
+ public void setNegativeButtonClickListener(OnClickListener onClickListener) {
+ setButton(Dialog.BUTTON_NEGATIVE,
+ getContext().getString(R.string.demo_user_inactivity_timeout_left_button),
+ onClickListener);
+ }
+
+ @Override
+ public void show() {
+ super.show();
+ final TextView messageView = (TextView) findViewById(R.id.message);
+ messageView.post(new Runnable() {
+ @Override
+ public void run() {
+ mCountDownTimer = new CountDownTimer(mCountDownDuration, mRefreshInterval) {
+
+ @Override
+ public void onTick(long millisUntilFinished) {
+ String msg = getContext().getString(
+ R.string.demo_user_inactivity_timeout_countdown,
+ millisUntilFinished / 1000);
+ messageView.setText(msg);
+ }
+
+ @Override
+ public void onFinish() {
+ dismiss();
+ if (mOnCountDownExpiredListener != null)
+ mOnCountDownExpiredListener.onCountDownExpired();
+ }
+ }.start();
+ }
+ });
+ }
+
+ @Override
+ public void onStop() {
+ if (mCountDownTimer != null) {
+ mCountDownTimer.cancel();
+ }
+ }
+
+ interface OnCountDownExpiredListener {
+ void onCountDownExpired();
+ }
+}
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 0437e1d..50e0662 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -12,6 +12,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := \
+ frameworks-base-testutils \
services.core \
services.devicepolicy \
services.net \
@@ -19,7 +20,8 @@
easymocklib \
guava \
android-support-test \
- mockito-target
+ mockito-target \
+ ShortcutManagerTestUtils
LOCAL_JAVA_LIBRARIES := android.test.runner
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 7017d81..b8ace28 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -108,9 +108,53 @@
<service android:name="com.android.server.job.MockPriorityJobService"
android:permission="android.permission.BIND_JOB_SERVICE" />
- <activity android:name="com.android.server.pm.ShortcutManagerTest$ShortcutActivity" />
- <activity android:name="com.android.server.pm.ShortcutManagerTest$ShortcutActivity2" />
- <activity android:name="com.android.server.pm.ShortcutManagerTest$ShortcutActivity3" />
+ <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity" />
+ <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity2" />
+ <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3" />
+
+ <activity android:name="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="true" android:exported="true" />
+
+ <activity-alias android:name="a.ShortcutEnabled"
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="true" android:exported="true">
+ </activity-alias>
+ <activity-alias android:name="a.ShortcutDisabled"
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="false" android:exported="true">
+ <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_5"/>
+ </activity-alias>
+ <activity-alias android:name="a.ShortcutUnexported"
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="true" android:exported="false">
+ <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_5"/>
+ </activity-alias>
+ <activity-alias android:name="a.Shortcut1"
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="true" android:exported="true">
+ <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_1"/>
+ </activity-alias>
+
+ <activity-alias android:name="a.DisabledMain"
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="false" android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity-alias>
+
+ <activity-alias android:name="a.UnexportedMain"
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="true" android:exported="false">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity-alias>
+
</application>
<instrumentation
diff --git a/services/tests/servicestests/assets/shortcut/dumpsys_expected.txt b/services/tests/servicestests/assets/shortcut/dumpsys_expected.txt
new file mode 100644
index 0000000..eed2087
--- /dev/null
+++ b/services/tests/servicestests/assets/shortcut/dumpsys_expected.txt
@@ -0,0 +1,105 @@
+{
+ "shortcut": [
+ {
+ "userId": 0,
+ "launchers": [
+ {
+ "name": "com.android.launcher.1"
+ },
+ {
+ "name": "com.android.launcher.2"
+ },
+ {
+ "name": "com.android.launcher.3"
+ },
+ {
+ "name": "com.android.launcher.4"
+ },
+ {
+ "name": "com.android.launcher.1"
+ }
+ ],
+ "packages": [
+ {
+ "name": "com.android.test.1",
+ "dynamic": 3,
+ "manifest": 0,
+ "pinned": 4,
+ "bitmaps": 0,
+ "bitmapBytes": 0
+ },
+ {
+ "name": "com.android.test.2",
+ "dynamic": 4,
+ "manifest": 0,
+ "pinned": 5,
+ "bitmaps": 2,
+ "bitmapBytes": ***BITMAP_SIZE***
+ },
+ {
+ "name": "com.android.test.3",
+ "dynamic": 3,
+ "manifest": 0,
+ "pinned": 6,
+ "bitmaps": 0,
+ "bitmapBytes": 0
+ },
+ {
+ "name": "com.android.test.4",
+ "dynamic": 0,
+ "manifest": 0,
+ "pinned": 0,
+ "bitmaps": 0,
+ "bitmapBytes": 0
+ }
+ ]
+ },
+ {
+ "userId": 10,
+ "launchers": [
+ {
+ "name": "com.android.launcher.1"
+ }
+ ],
+ "packages": [
+ {
+ "name": "com.android.test.1",
+ "dynamic": 3,
+ "manifest": 0,
+ "pinned": 2,
+ "bitmaps": 0,
+ "bitmapBytes": 0
+ }
+ ]
+ },
+ {
+ "userId": 20,
+ "launchers": [
+ {
+ "name": "com.android.launcher.1"
+ },
+ {
+ "name": "com.android.launcher.2"
+ },
+ {
+ "name": "com.android.launcher.3"
+ },
+ {
+ "name": "com.android.launcher.1"
+ }
+ ],
+ "packages": [
+ {
+ "name": "com.android.test.1",
+ "dynamic": 3,
+ "manifest": 0,
+ "pinned": 6,
+ "bitmaps": 0,
+ "bitmapBytes": 0
+ }
+ ]
+ }
+ ],
+ "lowRam": false,
+ "iconSize": 128
+}
diff --git a/services/tests/servicestests/assets/shortcut/shortcut_legacy_file.xml b/services/tests/servicestests/assets/shortcut/shortcut_legacy_file.xml
new file mode 100644
index 0000000..f7eee91
--- /dev/null
+++ b/services/tests/servicestests/assets/shortcut/shortcut_legacy_file.xml
@@ -0,0 +1,25 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<!-- 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.
+-->
+<user locales="en-US" last-app-scan-time="3113976673">
+ <package name="com.android.test.1" call-count="0" last-reset="1468976368772">
+ <package-info version="25" last_udpate_time="1230796800000" />
+ <shortcut id="manifest-shortcut-storage" activity="com.android.test.1/com.android.test.1.Settings" title="Storage" intent="#Intent;action=android.settings.INTERNAL_STORAGE_SETTINGS;end" timestamp="1469050672334" flags="1" >
+ <intent-extras>
+ <int name="key" value="12345" />
+ </intent-extras>
+ </shortcut>
+ </package>
+</user>
diff --git a/services/tests/servicestests/res/drawable/icon3.png b/services/tests/servicestests/res/drawable/icon3.png
new file mode 100644
index 0000000..64eb294
--- /dev/null
+++ b/services/tests/servicestests/res/drawable/icon3.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/night_mode_settings.xml b/services/tests/servicestests/res/values/strings.xml
similarity index 64%
copy from packages/SystemUI/res/layout/night_mode_settings.xml
copy to services/tests/servicestests/res/values/strings.xml
index 3725e78..2f9d06c 100644
--- a/packages/SystemUI/res/layout/night_mode_settings.xml
+++ b/services/tests/servicestests/res/values/strings.xml
@@ -14,11 +14,11 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <include layout="@layout/switch_bar" />
-
-</LinearLayout>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="shortcut_title1"></string>
+ <string name="shortcut_text1"></string>
+ <string name="shortcut_disabled_message1"></string>
+ <string name="shortcut_title2"></string>
+ <string name="shortcut_text2"></string>
+ <string name="shortcut_disabled_message2"></string>
+</resources>
diff --git a/services/tests/servicestests/res/xml/shortcut_0.xml b/services/tests/servicestests/res/xml/shortcut_0.xml
new file mode 100644
index 0000000..fda001e
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_0.xml
@@ -0,0 +1,2 @@
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+</shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_1.xml b/services/tests/servicestests/res/xml/shortcut_1.xml
new file mode 100644
index 0000000..e3f9172
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_1.xml
@@ -0,0 +1,18 @@
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutId="ms1"
+ android:enabled="true"
+ android:icon="@drawable/icon1"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ android:shortcutLongLabel="@string/shortcut_text1"
+ android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
+ >
+ <intent
+ android:action="action1"
+ android:data="data1"
+ >
+ </intent>
+ <categories android:name="android.shortcut.conversation" />
+ <categories android:name="android.shortcut.media" />
+ </shortcut>
+</shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_1_alt.xml b/services/tests/servicestests/res/xml/shortcut_1_alt.xml
new file mode 100644
index 0000000..2d5e8e7
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_1_alt.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutId="ms1-alt"
+ android:enabled="true"
+ android:icon="@drawable/icon1"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ android:shortcutLongLabel="@string/shortcut_text1"
+ android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
+ >
+ <intent
+ android:action="action1"
+ android:data="data1"
+ >
+ </intent>
+ <categories android:name="android.shortcut.conversation" />
+ <categories android:name="android.shortcut.media" />
+ </shortcut>
+</shortcuts>
diff --git a/packages/SystemUI/res/layout/night_mode_settings.xml b/services/tests/servicestests/res/xml/shortcut_1_disable.xml
similarity index 61%
copy from packages/SystemUI/res/layout/night_mode_settings.xml
copy to services/tests/servicestests/res/xml/shortcut_1_disable.xml
index 3725e78..e3ee3a0 100644
--- a/packages/SystemUI/res/layout/night_mode_settings.xml
+++ b/services/tests/servicestests/res/xml/shortcut_1_disable.xml
@@ -13,12 +13,13 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <include layout="@layout/switch_bar" />
-
-</LinearLayout>
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutId="ms1"
+ android:enabled="false"
+ android:icon="@drawable/icon2"
+ android:shortcutShortLabel="@string/shortcut_title2"
+ android:shortcutLongLabel="@string/shortcut_text2"
+ android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
+ />
+</shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_2.xml b/services/tests/servicestests/res/xml/shortcut_2.xml
new file mode 100644
index 0000000..f7ea803
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_2.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutId="ms1"
+ android:enabled="true"
+ android:icon="@drawable/icon1"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ android:shortcutLongLabel="@string/shortcut_text1"
+ android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
+ >
+ <intent
+ android:action="action1"
+ android:data="http://a.b.c/"
+ >
+ </intent>
+ <categories android:name="android.shortcut.conversation" />
+ <categories android:name="android.shortcut.media" />
+ </shortcut>
+ <shortcut
+ android:shortcutId="ms2"
+ android:enabled="true"
+ android:icon="@drawable/icon2"
+ android:shortcutShortLabel="@string/shortcut_title2"
+ android:shortcutLongLabel="@string/shortcut_text2"
+ android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
+ >
+ <intent
+ android:action="action2"
+ android:data="http://a.b.c/2"
+ >
+ </intent>
+ <categories android:name="android.shortcut.conversation" />
+ </shortcut>
+</shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_2_duplicate.xml b/services/tests/servicestests/res/xml/shortcut_2_duplicate.xml
new file mode 100644
index 0000000..b00ec60
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_2_duplicate.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutId="ms1"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ >
+ <intent
+ android:action="action1"
+ >
+ </intent>
+ </shortcut>
+ <shortcut
+ android:shortcutId="ms1"
+ android:shortcutShortLabel="@string/shortcut_title2"
+ >
+ <intent
+ android:action="action2"
+ >
+ </intent>
+ </shortcut>
+</shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_3.xml b/services/tests/servicestests/res/xml/shortcut_3.xml
new file mode 100644
index 0000000..432ca49
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_3.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutId="ms1"
+ android:enabled="true"
+ android:icon="@drawable/icon1"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ android:shortcutLongLabel="@string/shortcut_text1"
+ android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
+ >
+ <intent
+ android:action="action1"
+ android:data="http://a.b.c/"
+ >
+ </intent>
+ <categories android:name="android.shortcut.conversation" />
+ <categories android:name="android.shortcut.media" />
+ </shortcut>
+ <shortcut
+ android:shortcutId="ms2"
+ android:enabled="true"
+ android:icon="@drawable/icon2"
+ android:shortcutShortLabel="@string/shortcut_title2"
+ android:shortcutLongLabel="@string/shortcut_text2"
+ android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
+ >
+ <intent
+ android:action="action2"
+ android:data="http://a.b.c/2"
+ >
+ </intent>
+ <categories android:name="android.shortcut.conversation" />
+ </shortcut>
+ <shortcut
+ android:shortcutId="ms3"
+ android:enabled="true"
+ android:icon="@drawable/icon3"
+ android:shortcutShortLabel="@string/shortcut_title2"
+ >
+ <intent android:action="action3" />
+ </shortcut>
+</shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_5.xml b/services/tests/servicestests/res/xml/shortcut_5.xml
new file mode 100644
index 0000000..9551100
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_5.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutId="ms1"
+ android:enabled="true"
+ android:icon="@drawable/icon1"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ android:shortcutLongLabel="@string/shortcut_text1"
+ android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
+ >
+ <intent
+ android:action="action1"
+ android:data="http://a.b.c/1"
+ >
+ </intent>
+ <categories android:name="android.shortcut.conversation" />
+ <categories android:name="android.shortcut.media" />
+ </shortcut>
+ <shortcut
+ android:shortcutId="ms2"
+ android:enabled="true"
+ android:icon="@drawable/icon2"
+ android:shortcutShortLabel="@string/shortcut_title2"
+ android:shortcutLongLabel="@string/shortcut_text2"
+ android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
+ >
+ <intent
+ android:action="action2"
+ >
+ </intent>
+ <categories android:name="android.shortcut.conversation" />
+ </shortcut>
+ <shortcut
+ android:shortcutId="ms3"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ >
+ <intent
+ android:action="android.intent.action.VIEW"
+ >
+ </intent>
+ </shortcut>
+ <shortcut
+ android:shortcutId="ms4"
+ android:shortcutShortLabel="@string/shortcut_title2"
+ >
+ <intent
+ android:action="android.intent.action.VIEW2"
+ >
+ </intent>
+ <categories />
+ <categories android:name="" />
+ <categories android:name="cat" />
+ </shortcut>
+ <shortcut
+ android:shortcutId="ms5"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ >
+ <intent
+ android:action="action"
+ android:data="http://www/"
+ android:targetPackage="abc"
+ android:targetClass=".xyz"
+ android:mimeType="foo/bar"
+ >
+ <categories android:name="cat1" />
+ <categories android:name="cat2" />
+ <extra android:name="key1" android:value="value1" />
+ <extra android:name="key2" android:value="value2" />
+ </intent>
+ </shortcut>
+</shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_5_alt.xml b/services/tests/servicestests/res/xml/shortcut_5_alt.xml
new file mode 100644
index 0000000..f79cd6f
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_5_alt.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutId="ms1_alt"
+ android:enabled="true"
+ android:icon="@drawable/icon1"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ android:shortcutLongLabel="@string/shortcut_text1"
+ android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
+ >
+ <intent
+ android:action="action1"
+ android:data="http://a.b.c/1"
+ >
+ </intent>
+ <categories android:name="android.shortcut.conversation" />
+ <categories android:name="android.shortcut.media" />
+ </shortcut>
+ <shortcut
+ android:shortcutId="ms2_alt"
+ android:enabled="true"
+ android:icon="@drawable/icon2"
+ android:shortcutShortLabel="@string/shortcut_title2"
+ android:shortcutLongLabel="@string/shortcut_text2"
+ android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
+ >
+ <intent
+ android:action="action2"
+ >
+ </intent>
+ <categories android:name="android.shortcut.conversation" />
+ </shortcut>
+ <shortcut
+ android:shortcutId="ms3_alt"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ >
+ <intent
+ android:action="android.intent.action.VIEW"
+ >
+ </intent>
+ </shortcut>
+ <shortcut
+ android:shortcutId="ms4_alt"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ >
+ <intent
+ android:action="android.intent.action.VIEW"
+ >
+ </intent>
+ </shortcut>
+ <shortcut
+ android:shortcutId="ms5_alt"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ >
+ <intent
+ android:action="android.intent.action.VIEW"
+ >
+ </intent>
+ </shortcut>
+</shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_5_reverse.xml b/services/tests/servicestests/res/xml/shortcut_5_reverse.xml
new file mode 100644
index 0000000..d5b7c8f
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_5_reverse.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutId="ms5"
+ android:enabled="true"
+ android:icon="@drawable/icon1"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ android:shortcutLongLabel="@string/shortcut_text1"
+ android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
+ >
+ <intent
+ android:action="action1"
+ android:data="http://a.b.c/1"
+ >
+ </intent>
+ <categories android:name="android.shortcut.conversation" />
+ <categories android:name="android.shortcut.media" />
+ </shortcut>
+ <shortcut
+ android:shortcutId="ms4"
+ android:enabled="true"
+ android:icon="@drawable/icon2"
+ android:shortcutShortLabel="@string/shortcut_title2"
+ android:shortcutLongLabel="@string/shortcut_text2"
+ android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
+ >
+ <intent
+ android:action="action2"
+ >
+ </intent>
+ <categories android:name="android.shortcut.conversation" />
+ </shortcut>
+ <shortcut
+ android:shortcutId="ms3"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ >
+ <intent
+ android:action="android.intent.action.VIEW"
+ >
+ </intent>
+ </shortcut>
+ <shortcut
+ android:shortcutId="ms2"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ >
+ <intent
+ android:action="android.intent.action.VIEW"
+ >
+ </intent>
+ </shortcut>
+ <shortcut
+ android:shortcutId="ms1"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ >
+ <intent
+ android:action="action"
+ >
+ <categories android:name="cat1" />
+ <categories android:name="cat2" />
+ <extra android:name="key1" android:value="value1" />
+ <extra android:name="key2" android:value="value2" />
+ </intent>
+ </shortcut>
+</shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_error_1.xml b/services/tests/servicestests/res/xml/shortcut_error_1.xml
new file mode 100644
index 0000000..3990d02
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_error_1.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutShortLabel="@string/shortcut_title1"
+ >
+ <intent
+ android:action="android.intent.action.VIEW"
+ >
+ </intent>
+ </shortcut>
+ <shortcut
+ android:shortcutId="x1"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ >
+ <intent
+ android:action="android.intent.action.VIEW"
+ >
+ </intent>
+ </shortcut>
+</shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_error_2.xml b/services/tests/servicestests/res/xml/shortcut_error_2.xml
new file mode 100644
index 0000000..a6f7150
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_error_2.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutId="manifest-shortcut-3"
+ >
+ <intent
+ android:action="android.intent.action.VIEW"
+ >
+ </intent>
+ </shortcut>
+ <shortcut
+ android:shortcutId="x2"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ >
+ <intent
+ android:action="android.intent.action.VIEW"
+ >
+ </intent>
+ </shortcut>
+</shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_error_3.xml b/services/tests/servicestests/res/xml/shortcut_error_3.xml
new file mode 100644
index 0000000..24ee024
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_error_3.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutId="manifest-shortcut-3"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ />
+ <shortcut
+ android:shortcutId="@string/shortcut_title1"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ />
+ <shortcut
+ android:shortcutId="x3"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ >
+ <intent
+ android:action="android.intent.action.VIEW"
+ >
+ </intent>
+ <categories android:name="@string/shortcut_title1" />
+ <categories android:name="cat2" />
+ </shortcut>
+</shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_error_4.xml b/services/tests/servicestests/res/xml/shortcut_error_4.xml
new file mode 100644
index 0000000..f680e99
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_error_4.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <!-- This is valid -->
+ <shortcut
+ android:shortcutId="ms1"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ >
+ <intent android:action="action1" >
+ <extra android:name="key1" android:value="value1" />
+ </intent>
+ </shortcut>
+
+ <!-- Invalid: no intent -->
+ <shortcut
+ android:shortcutId="ms_ignored1"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ />
+
+ <!-- Valid: more than one intent -->
+ <shortcut
+ android:shortcutId="ms2"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ >
+ <intent android:action="action2_1" >
+ <extra android:name="key1" android:value="value1" />
+ </intent>
+ <intent android:action="action2_2">
+ <extra android:name="key2" android:value="value2" />
+ </intent>
+ </shortcut>
+
+ <!-- Valid: disabled shortcut doesn't need an intent -->
+ <shortcut
+ android:shortcutId="ms3"
+ android:enabled="false"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ />
+
+ <!-- Valid, but disabled shortcut's intent will be ignored. -->
+ <shortcut
+ android:shortcutId="ms4"
+ android:enabled="false"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ >
+ <intent android:action="action4" />
+ </shortcut>
+
+ <!-- Invalid, no intent action (if any of the intents is invalid, the entire shortcut will be invalid.) -->
+ <shortcut
+ android:shortcutId="ms_ignored2"
+ android:shortcutShortLabel="@string/shortcut_title1"
+ >
+ <intent android:data="x"/>
+ <intent android:action="actionx"/>
+ </shortcut>
+</shortcuts>
diff --git a/services/tests/servicestests/src/android/net/apf/ApfTest.java b/services/tests/servicestests/src/android/net/apf/ApfTest.java
index 8ac238a..bd76118 100644
--- a/services/tests/servicestests/src/android/net/apf/ApfTest.java
+++ b/services/tests/servicestests/src/android/net/apf/ApfTest.java
@@ -26,14 +26,23 @@
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
import android.net.ip.IpManager;
+import android.net.metrics.IpConnectivityLog;
+import android.net.metrics.RaEvent;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.os.ConditionVariable;
+import android.os.Parcelable;
import android.system.ErrnoException;
import android.system.Os;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.verify;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
@@ -43,6 +52,7 @@
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.nio.ByteBuffer;
+import java.util.List;
import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -56,9 +66,12 @@
public class ApfTest extends AndroidTestCase {
private static final int TIMEOUT_MS = 500;
+ @Mock IpConnectivityLog mLog;
+
@Override
public void setUp() throws Exception {
super.setUp();
+ MockitoAnnotations.initMocks(this);
// Load up native shared library containing APF interpreter exposed via JNI.
System.loadLibrary("servicestestsjni");
}
@@ -70,6 +83,9 @@
// least the minimum packet size.
private final static int MIN_PKT_SIZE = 15;
+ private final static boolean DROP_MULTICAST = true;
+ private final static boolean ALLOW_MULTICAST = false;
+
private void assertVerdict(int expected, byte[] program, byte[] packet, int filterAge) {
assertEquals(expected, apfSimulate(program, packet, filterAge));
}
@@ -562,10 +578,10 @@
public final static byte[] MOCK_MAC_ADDR = new byte[]{1,2,3,4,5,6};
private FileDescriptor mWriteSocket;
- public TestApfFilter(IpManager.Callback ipManagerCallback, boolean multicastFilter) throws
- Exception {
- super(new ApfCapabilities(2, 1000, ARPHRD_ETHER), NetworkInterface.getByName("lo"),
- ipManagerCallback, multicastFilter);
+ public TestApfFilter(IpManager.Callback ipManagerCallback, boolean multicastFilter,
+ IpConnectivityLog log) throws Exception {
+ super(new ApfCapabilities(2, 1536, ARPHRD_ETHER), NetworkInterface.getByName("lo"),
+ ipManagerCallback, multicastFilter, log);
}
// Pretend an RA packet has been received and show it to ApfFilter.
@@ -602,6 +618,7 @@
}
private static final int ETH_HEADER_LEN = 14;
+ private static final int ETH_DEST_ADDR_OFFSET = 0;
private static final int ETH_ETHERTYPE_OFFSET = 12;
private static final byte[] ETH_BROADCAST_MAC_ADDRESS = new byte[]{
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
@@ -652,7 +669,7 @@
private static final int DHCP_CLIENT_PORT = 68;
private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 48;
- private static int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
+ private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
private static final byte[] ARP_IPV4_REQUEST_HEADER = new byte[]{
0, 1, // Hardware type: Ethernet (1)
8, 0, // Protocol type: IP (0x0800)
@@ -660,14 +677,23 @@
4, // Protocol size: 4
0, 1 // Opcode: request (1)
};
- private static int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
+ private static final byte[] ARP_IPV4_REPLY_HEADER = new byte[]{
+ 0, 1, // Hardware type: Ethernet (1)
+ 8, 0, // Protocol type: IP (0x0800)
+ 6, // Hardware size: 6
+ 4, // Protocol size: 4
+ 0, 2 // Opcode: reply (2)
+ };
+ private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
- private static byte[] MOCK_IPV4_ADDR = new byte[]{10, 0, 0, 1};
+ private static final byte[] MOCK_IPV4_ADDR = new byte[]{10, 0, 0, 1};
+ private static final byte[] ANOTHER_IPV4_ADDR = new byte[]{10, 0, 0, 2};
+ private static final byte[] IPV4_ANY_HOST_ADDR = new byte[]{0, 0, 0, 0};
@LargeTest
public void testApfFilterIPv4() throws Exception {
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
- ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
+ ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, mLog);
byte[] program = ipManagerCallback.getApfProgram();
// Verify empty packet of 100 zero bytes is passed
@@ -699,7 +725,7 @@
@LargeTest
public void testApfFilterIPv6() throws Exception {
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
- ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
+ ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, mLog);
byte[] program = ipManagerCallback.getApfProgram();
// Verify empty IPv6 packet is passed
@@ -726,7 +752,7 @@
@LargeTest
public void testApfFilterMulticast() throws Exception {
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
- ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
+ ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, mLog);
byte[] program = ipManagerCallback.getApfProgram();
// Construct IPv4 and IPv6 multicast packets.
@@ -772,7 +798,7 @@
// Verify it can be initialized to on
ipManagerCallback.resetApfProgramWait();
apfFilter.shutdown();
- apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
+ apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, mLog);
program = ipManagerCallback.getApfProgram();
assertDrop(program, bcastv4packet.array(), 0);
assertDrop(program, mcastv4packet.array(), 0);
@@ -785,51 +811,81 @@
apfFilter.shutdown();
}
- private void verifyArpFilter(MockIpManagerCallback ipManagerCallback, ApfFilter apfFilter,
- LinkProperties linkProperties, int filterResult) {
- ipManagerCallback.resetApfProgramWait();
- apfFilter.setLinkProperties(linkProperties);
- byte[] program = ipManagerCallback.getApfProgram();
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
- assertPass(program, packet.array(), 0);
- packet.position(ARP_HEADER_OFFSET);
- packet.put(ARP_IPV4_REQUEST_HEADER);
- assertVerdict(filterResult, program, packet.array(), 0);
- packet.position(ARP_TARGET_IP_ADDRESS_OFFSET);
- packet.put(MOCK_IPV4_ADDR);
- assertPass(program, packet.array(), 0);
+ private byte[] getProgram(MockIpManagerCallback cb, ApfFilter filter, LinkProperties lp) {
+ cb.resetApfProgramWait();
+ filter.setLinkProperties(lp);
+ return cb.getApfProgram();
+ }
+
+ private void verifyArpFilter(byte[] program, int filterResult) {
+ // Verify ARP request packet
+ assertPass(program, arpRequestBroadcast(MOCK_IPV4_ADDR), 0);
+ assertVerdict(filterResult, program, arpRequestBroadcast(ANOTHER_IPV4_ADDR), 0);
+ assertDrop(program, arpRequestBroadcast(IPV4_ANY_HOST_ADDR), 0);
+
+ // Verify unicast ARP reply packet is always accepted.
+ assertPass(program, arpReplyUnicast(MOCK_IPV4_ADDR), 0);
+ assertPass(program, arpReplyUnicast(ANOTHER_IPV4_ADDR), 0);
+ assertPass(program, arpReplyUnicast(IPV4_ANY_HOST_ADDR), 0);
+
+ // Verify GARP reply packets are always filtered
+ assertDrop(program, garpReply(), 0);
}
@LargeTest
public void testApfFilterArp() throws Exception {
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
- ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
- byte[] program = ipManagerCallback.getApfProgram();
+ ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, mLog);
- // Verify initially ARP filter is off
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
- assertPass(program, packet.array(), 0);
- packet.position(ARP_HEADER_OFFSET);
- packet.put(ARP_IPV4_REQUEST_HEADER);
- assertPass(program, packet.array(), 0);
- packet.position(ARP_TARGET_IP_ADDRESS_OFFSET);
- packet.put(MOCK_IPV4_ADDR);
- assertPass(program, packet.array(), 0);
+ // Verify initially ARP request filter is off, and GARP filter is on.
+ verifyArpFilter(ipManagerCallback.getApfProgram(), PASS);
// Inform ApfFilter of our address and verify ARP filtering is on
+ LinkAddress linkAddress = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 24);
LinkProperties lp = new LinkProperties();
- assertTrue(lp.addLinkAddress(
- new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 24)));
- verifyArpFilter(ipManagerCallback, apfFilter, lp, DROP);
+ assertTrue(lp.addLinkAddress(linkAddress));
+ verifyArpFilter(getProgram(ipManagerCallback, apfFilter, lp), DROP);
// Inform ApfFilter of loss of IP and verify ARP filtering is off
- verifyArpFilter(ipManagerCallback, apfFilter, new LinkProperties(), PASS);
+ verifyArpFilter(getProgram(ipManagerCallback, apfFilter, new LinkProperties()), PASS);
apfFilter.shutdown();
}
+ private static byte[] arpRequestBroadcast(byte[] tip) {
+ ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
+ packet.position(ETH_DEST_ADDR_OFFSET);
+ packet.put(ETH_BROADCAST_MAC_ADDRESS);
+ packet.position(ARP_HEADER_OFFSET);
+ packet.put(ARP_IPV4_REQUEST_HEADER);
+ packet.position(ARP_TARGET_IP_ADDRESS_OFFSET);
+ packet.put(tip);
+ return packet.array();
+ }
+
+ private static byte[] arpReplyUnicast(byte[] tip) {
+ ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
+ packet.position(ARP_HEADER_OFFSET);
+ packet.put(ARP_IPV4_REPLY_HEADER);
+ packet.position(ARP_TARGET_IP_ADDRESS_OFFSET);
+ packet.put(tip);
+ return packet.array();
+ }
+
+ private static byte[] garpReply() {
+ ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
+ packet.position(ETH_DEST_ADDR_OFFSET);
+ packet.put(ETH_BROADCAST_MAC_ADDRESS);
+ packet.position(ARP_HEADER_OFFSET);
+ packet.put(ARP_IPV4_REPLY_HEADER);
+ packet.position(ARP_TARGET_IP_ADDRESS_OFFSET);
+ packet.put(IPV4_ANY_HOST_ADDR);
+ return packet.array();
+ }
+
// Verify that the last program pushed to the IpManager.Callback properly filters the
// given packet for the given lifetime.
private void verifyRaLifetime(MockIpManagerCallback ipManagerCallback, ByteBuffer packet,
@@ -867,6 +923,35 @@
verifyRaLifetime(ipManagerCallback, packet, lifetime);
}
+ private void verifyRaEvent(RaEvent expected) {
+ ArgumentCaptor<Parcelable> captor = ArgumentCaptor.forClass(Parcelable.class);
+ verify(mLog, atLeastOnce()).log(captor.capture());
+ RaEvent got = lastRaEvent(captor.getAllValues());
+ if (!raEventEquals(expected, got)) {
+ assertEquals(expected, got); // fail for printing an assertion error message.
+ }
+ }
+
+ private RaEvent lastRaEvent(List<Parcelable> events) {
+ RaEvent got = null;
+ for (Parcelable ev : events) {
+ if (ev instanceof RaEvent) {
+ got = (RaEvent) ev;
+ }
+ }
+ return got;
+ }
+
+ private boolean raEventEquals(RaEvent ev1, RaEvent ev2) {
+ return (ev1 != null) && (ev2 != null)
+ && (ev1.routerLifetime == ev2.routerLifetime)
+ && (ev1.prefixValidLifetime == ev2.prefixValidLifetime)
+ && (ev1.prefixPreferredLifetime == ev2.prefixPreferredLifetime)
+ && (ev1.routeInfoLifetime == ev2.routeInfoLifetime)
+ && (ev1.rdnssLifetime == ev2.rdnssLifetime)
+ && (ev1.dnsslLifetime == ev2.dnsslLifetime);
+ }
+
private void assertInvalidRa(TestApfFilter apfFilter, MockIpManagerCallback ipManagerCallback,
ByteBuffer packet) throws IOException, ErrnoException {
ipManagerCallback.resetApfProgramWait();
@@ -877,7 +962,7 @@
@LargeTest
public void testApfFilterRa() throws Exception {
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
- TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
+ TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, mLog);
byte[] program = ipManagerCallback.getApfProgram();
// Verify RA is passed the first time
@@ -891,6 +976,7 @@
assertPass(program, basePacket.array(), 0);
testRaLifetime(apfFilter, ipManagerCallback, basePacket, 1000);
+ verifyRaEvent(new RaEvent(1000, -1, -1, -1, -1, -1));
// Ensure zero-length options cause the packet to be silently skipped.
// Do this before we test other packets. http://b/29586253
@@ -916,6 +1002,7 @@
prefixOptionPacket.putInt(
ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET, 200);
testRaLifetime(apfFilter, ipManagerCallback, prefixOptionPacket, 100);
+ verifyRaEvent(new RaEvent(1000, 200, 100, -1, -1, -1));
ByteBuffer rdnssOptionPacket = ByteBuffer.wrap(
new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
@@ -926,6 +1013,7 @@
rdnssOptionPacket.putInt(
ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, 300);
testRaLifetime(apfFilter, ipManagerCallback, rdnssOptionPacket, 300);
+ verifyRaEvent(new RaEvent(1000, -1, -1, -1, 300, -1));
ByteBuffer routeInfoOptionPacket = ByteBuffer.wrap(
new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
@@ -936,6 +1024,7 @@
routeInfoOptionPacket.putInt(
ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, 400);
testRaLifetime(apfFilter, ipManagerCallback, routeInfoOptionPacket, 400);
+ verifyRaEvent(new RaEvent(1000, -1, -1, 400, -1, -1));
ByteBuffer dnsslOptionPacket = ByteBuffer.wrap(
new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
@@ -948,6 +1037,7 @@
// Note that lifetime of 2000 will be ignored in favor of shorter
// route lifetime of 1000.
testRaLifetime(apfFilter, ipManagerCallback, dnsslOptionPacket, 1000);
+ verifyRaEvent(new RaEvent(1000, -1, -1, -1, -1, 2000));
// Verify that current program filters all five RAs:
verifyRaLifetime(ipManagerCallback, basePacket, 1000);
diff --git a/services/tests/servicestests/src/android/net/metrics/IpConnectivityLogTest.java b/services/tests/servicestests/src/android/net/metrics/IpConnectivityLogTest.java
new file mode 100644
index 0000000..1433f95
--- /dev/null
+++ b/services/tests/servicestests/src/android/net/metrics/IpConnectivityLogTest.java
@@ -0,0 +1,162 @@
+/*
+ * 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.net.metrics;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.net.ConnectivityMetricsEvent;
+import android.net.IConnectivityMetricsLogger;
+
+import junit.framework.TestCase;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+
+public class IpConnectivityLogTest extends TestCase {
+
+ // use same Parcel object everywhere for pointer equality
+ static final Bundle FAKE_EV = new Bundle();
+
+ @Mock IConnectivityMetricsLogger mService;
+ ArgumentCaptor<ConnectivityMetricsEvent> evCaptor;
+
+ IpConnectivityLog mLog;
+
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ evCaptor = ArgumentCaptor.forClass(ConnectivityMetricsEvent.class);
+ mLog = new IpConnectivityLog(mService);
+ }
+
+ public void testLogEvents() throws Exception {
+ assertTrue(mLog.log(1, FAKE_EV));
+ assertTrue(mLog.log(2, FAKE_EV));
+ assertTrue(mLog.log(3, FAKE_EV));
+
+ List<ConnectivityMetricsEvent> gotEvents = verifyEvents(3);
+ assertEventsEqual(expectedEvent(1), gotEvents.get(0));
+ assertEventsEqual(expectedEvent(2), gotEvents.get(1));
+ assertEventsEqual(expectedEvent(3), gotEvents.get(2));
+ }
+
+ public void testLogEventTriggerThrottling() throws Exception {
+ when(mService.logEvent(any())).thenReturn(1234L);
+
+ assertFalse(mLog.log(1, FAKE_EV));
+ }
+
+ public void testLogEventFails() throws Exception {
+ when(mService.logEvent(any())).thenReturn(-1L); // Error.
+
+ assertFalse(mLog.log(1, FAKE_EV));
+ }
+
+ public void testLogEventWhenThrottling() throws Exception {
+ when(mService.logEvent(any())).thenReturn(Long.MAX_VALUE); // Throttled
+
+ // No events are logged. The service is only called once
+ // After that, throttling state is maintained locally.
+ assertFalse(mLog.log(1, FAKE_EV));
+ assertFalse(mLog.log(2, FAKE_EV));
+
+ List<ConnectivityMetricsEvent> gotEvents = verifyEvents(1);
+ assertEventsEqual(expectedEvent(1), gotEvents.get(0));
+ }
+
+ public void testLogEventRecoverFromThrottling() throws Exception {
+ final long throttleTimeout = System.currentTimeMillis() + 50;
+ when(mService.logEvent(any())).thenReturn(throttleTimeout, 0L);
+
+ assertFalse(mLog.log(1, FAKE_EV));
+ new Thread() {
+ public void run() {
+ busySpinLog();
+ }
+ }.start();
+
+ List<ConnectivityMetricsEvent> gotEvents = verifyEvents(2, 200);
+ assertEventsEqual(expectedEvent(1), gotEvents.get(0));
+ assertEventsEqual(expectedEvent(2), gotEvents.get(1));
+ }
+
+ public void testLogEventRecoverFromThrottlingWithMultipleCallers() throws Exception {
+ final long throttleTimeout = System.currentTimeMillis() + 50;
+ when(mService.logEvent(any())).thenReturn(throttleTimeout, 0L);
+
+ assertFalse(mLog.log(1, FAKE_EV));
+ final int nCallers = 10;
+ for (int i = 0; i < nCallers; i++) {
+ new Thread() {
+ public void run() {
+ busySpinLog();
+ }
+ }.start();
+ }
+
+ List<ConnectivityMetricsEvent> gotEvents = verifyEvents(1 + nCallers, 200);
+ assertEventsEqual(expectedEvent(1), gotEvents.get(0));
+ for (int i = 0; i < nCallers; i++) {
+ assertEventsEqual(expectedEvent(2), gotEvents.get(1 + i));
+ }
+ }
+
+ void busySpinLog() {
+ final long timeout = 200;
+ final long stop = System.currentTimeMillis() + timeout;
+ try {
+ while (System.currentTimeMillis() < stop) {
+ if (mLog.log(2, FAKE_EV)) {
+ return;
+ }
+ Thread.sleep(10);
+ }
+ } catch (InterruptedException e) { }
+ }
+
+ List<ConnectivityMetricsEvent> verifyEvents(int n) throws Exception {
+ verify(mService, times(n)).logEvent(evCaptor.capture());
+ return evCaptor.getAllValues();
+ }
+
+ List<ConnectivityMetricsEvent> verifyEvents(int n, int timeoutMs) throws Exception {
+ verify(mService, timeout(timeoutMs).times(n)).logEvent(evCaptor.capture());
+ return evCaptor.getAllValues();
+ }
+
+ static ConnectivityMetricsEvent expectedEvent(int timestamp) {
+ return new ConnectivityMetricsEvent((long)timestamp, 0, 0, FAKE_EV);
+ }
+
+ /** Outer equality for ConnectivityMetricsEvent to avoid overriding equals() and hashCode(). */
+ static void assertEventsEqual(ConnectivityMetricsEvent expected, ConnectivityMetricsEvent got) {
+ assertEquals(expected.timestamp, got.timestamp);
+ assertEquals(expected.componentTag, got.componentTag);
+ assertEquals(expected.eventTag, got.eventTag);
+ assertEquals(expected.data, got.data);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProvider.java b/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProvider.java
new file mode 100644
index 0000000..808f4dd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProvider.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.test.mock.MockContentProvider;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * Fake for system settings.
+ *
+ * To use, ensure that the Context used by the test code returns a ContentResolver that uses this
+ * provider for the Settings authority:
+ *
+ * class MyTestContext extends MockContext {
+ * ...
+ * private final MockContentResolver mContentResolver;
+ * public MyTestContext(...) {
+ * ...
+ * mContentResolver = new MockContentResolver();
+ * mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ * }
+ * ...
+ * @Override
+ * public ContentResolver getContentResolver() {
+ * return mContentResolver;
+ * }
+ *
+ * As long as the code under test is using the test Context, the actual code under test does not
+ * need to be modified, and can access Settings using the normal static methods:
+ *
+ * Settings.Global.getInt(cr, "my_setting", 0); // Returns 0.
+ * Settings.Global.putInt(cr, "my_setting", 5);
+ * Settings.Global.getInt(cr, "my_setting", 0); // Returns 5.
+ *
+ * Note that this class cannot be used in the same process as real settings. This is because it
+ * works by passing an alternate ContentResolver to Settings operations. Unfortunately, the Settings
+ * class only fetches the content provider from the passed-in ContentResolver the first time it's
+ * used, and after that stores it in a per-process static.
+ *
+ * TODO: evaluate implementing settings change notifications. This would require:
+ *
+ * 1. Making ContentResolver#registerContentObserver non-final and overriding it in
+ * MockContentResolver.
+ * 2. Making FakeSettingsProvider take a ContentResolver argument.
+ * 3. Calling ContentResolver#notifyChange(getUriFor(table, arg), ...) on every settings change.
+ */
+public class FakeSettingsProvider extends MockContentProvider {
+
+ private static final String TAG = FakeSettingsProvider.class.getSimpleName();
+ private static final boolean DBG = false;
+ private static final String[] TABLES = { "system", "secure", "global" };
+
+ private final HashMap<String, HashMap<String, String>> mTables = new HashMap<>();
+
+ public FakeSettingsProvider() {
+ for (int i = 0; i < TABLES.length; i++) {
+ mTables.put(TABLES[i], new HashMap<String, String>());
+ }
+ }
+
+ private Uri getUriFor(String table, String key) {
+ switch (table) {
+ case "system":
+ return Settings.System.getUriFor(key);
+ case "secure":
+ return Settings.Secure.getUriFor(key);
+ case "global":
+ return Settings.Global.getUriFor(key);
+ default:
+ throw new UnsupportedOperationException("Unknown settings table " + table);
+ }
+ }
+
+ public Bundle call(String method, String arg, Bundle extras) {
+ // Methods are "GET_system", "GET_global", "PUT_secure", etc.
+ String[] commands = method.split("_", 2);
+ String op = commands[0];
+ String table = commands[1];
+
+ Bundle out = new Bundle();
+ String value;
+ switch (op) {
+ case "GET":
+ value = mTables.get(table).get(arg);
+ if (value != null) {
+ if (DBG) {
+ Log.d(TAG, String.format("Returning fake setting %s.%s = %s",
+ table, arg, value));
+ }
+ out.putString(Settings.NameValueTable.VALUE, value);
+ }
+ break;
+ case "PUT":
+ value = extras.getString(Settings.NameValueTable.VALUE, null);
+ if (DBG) {
+ Log.d(TAG, String.format("Inserting fake setting %s.%s = %s",
+ table, arg, value));
+ }
+ if (value != null) {
+ mTables.get(table).put(arg, value);
+ } else {
+ mTables.get(table).remove(arg);
+ }
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown command " + method);
+ }
+
+ return out;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProviderTest.java b/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProviderTest.java
new file mode 100644
index 0000000..05de0a5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProviderTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.provider.Settings;
+import android.test.AndroidTestCase;
+import android.test.mock.MockContentResolver;
+import android.test.mock.MockContext;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Unit tests for FakeSettingsProvider.
+ */
+public class FakeSettingsProviderTest extends AndroidTestCase {
+
+ private MockContentResolver mCr;
+
+ @Override
+ public void setUp() throws Exception {
+ mCr = new MockContentResolver();
+ mCr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ }
+
+ @SmallTest
+ public void testBasicOperation() throws Exception {
+ String settingName = Settings.System.SCREEN_BRIGHTNESS;
+
+ try {
+ Settings.System.getInt(mCr, settingName);
+ fail("FakeSettingsProvider should start off empty.");
+ } catch (Settings.SettingNotFoundException expected) {}
+
+ // Check that fake settings can be written and read back.
+ Settings.System.putInt(mCr, settingName, 123);
+ assertEquals(123, Settings.System.getInt(mCr, settingName));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 4fae4a7..59ccbd9 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -17,6 +17,7 @@
package com.android.server;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.getNetworkTypeName;
@@ -26,6 +27,7 @@
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
@@ -49,6 +51,7 @@
import android.net.NetworkMisc;
import android.net.NetworkRequest;
import android.net.RouteInfo;
+import android.net.metrics.IpConnectivityLog;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
@@ -61,12 +64,15 @@
import android.os.MessageQueue.IdleHandler;
import android.os.Process;
import android.os.SystemClock;
+import android.provider.Settings;
import android.test.AndroidTestCase;
+import android.test.mock.MockContentResolver;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import android.util.LogPrinter;
+import com.android.internal.util.FakeSettingsProvider;
import com.android.internal.util.WakeupMessage;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkMonitor;
@@ -75,6 +81,7 @@
import java.net.InetAddress;
import java.util.ArrayList;
+import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -90,12 +97,14 @@
private static final String TAG = "ConnectivityServiceTest";
private static final int TIMEOUT_MS = 500;
+ private static final int TEST_LINGER_DELAY_MS = 120;
private BroadcastInterceptingContext mServiceContext;
private WrappedConnectivityService mService;
private WrappedConnectivityManager mCm;
private MockNetworkAgent mWiFiNetworkAgent;
private MockNetworkAgent mCellNetworkAgent;
+ private MockNetworkAgent mEthernetNetworkAgent;
// This class exists to test bindProcessToNetwork and getBoundNetworkForProcess. These methods
// do not go through ConnectivityService but talk to netd directly, so they don't automatically
@@ -118,27 +127,24 @@
}
private class MockContext extends BroadcastInterceptingContext {
+ private final MockContentResolver mContentResolver;
+
MockContext(Context base) {
super(base);
+ mContentResolver = new MockContentResolver();
+ mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
}
@Override
- public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
- // PendingIntents sent by the AlarmManager are not intercepted by
- // BroadcastInterceptingContext so we must really register the receiver.
- // This shouldn't effect the real NetworkMonitors as the action contains a random token.
- if (filter.getAction(0).startsWith("android.net.netmon.lingerExpired")) {
- return getBaseContext().registerReceiver(receiver, filter);
- } else {
- return super.registerReceiver(receiver, filter);
- }
- }
-
- @Override
- public Object getSystemService (String name) {
+ public Object getSystemService(String name) {
if (name == Context.CONNECTIVITY_SERVICE) return mCm;
return super.getSystemService(name);
}
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
}
/**
@@ -242,6 +248,9 @@
mNetworkCapabilities = new NetworkCapabilities();
mNetworkCapabilities.addTransportType(transport);
switch (transport) {
+ case TRANSPORT_ETHERNET:
+ mScore = 70;
+ break;
case TRANSPORT_WIFI:
mScore = 60;
break;
@@ -303,6 +312,11 @@
mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
}
+ public void removeCapability(int capability) {
+ mNetworkCapabilities.removeCapability(capability);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
public void setSignalStrength(int signalStrength) {
mNetworkCapabilities.setSignalStrength(signalStrength);
mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
@@ -318,7 +332,8 @@
* @param validated Indicate if network should pretend to be validated.
*/
public void connect(boolean validated) {
- assertEquals(mNetworkInfo.getDetailedState(), DetailedState.IDLE);
+ assertEquals("MockNetworkAgents can only be connected once",
+ mNetworkInfo.getDetailedState(), DetailedState.IDLE);
assertFalse(mNetworkCapabilities.hasCapability(NET_CAPABILITY_INTERNET));
NetworkCallback callback = null;
@@ -536,6 +551,11 @@
super(context, handler, cmdName, cmd);
}
+ public FakeWakeupMessage(Context context, Handler handler, String cmdName, int cmd,
+ int arg1, int arg2, Object obj) {
+ super(context, handler, cmdName, cmd, arg1, arg2, obj);
+ }
+
@Override
public void schedule(long when) {
long delayMs = when - SystemClock.elapsedRealtime();
@@ -544,12 +564,13 @@
fail("Attempting to send msg more than " + UNREASONABLY_LONG_WAIT +
"ms into the future: " + delayMs);
}
- mHandler.sendEmptyMessageDelayed(mCmd, delayMs);
+ Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2, mObj);
+ mHandler.sendMessageDelayed(msg, delayMs);
}
@Override
public void cancel() {
- mHandler.removeMessages(mCmd);
+ mHandler.removeMessages(mCmd, mObj);
}
@Override
@@ -565,28 +586,25 @@
public String gen204ProbeRedirectUrl = null;
public WrappedNetworkMonitor(Context context, Handler handler,
- NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest) {
- super(context, handler, networkAgentInfo, defaultRequest);
+ NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest,
+ IpConnectivityLog log) {
+ super(context, handler, networkAgentInfo, defaultRequest, log);
}
@Override
protected CaptivePortalProbeResult isCaptivePortal() {
return new CaptivePortalProbeResult(gen204ProbeResult, gen204ProbeRedirectUrl);
}
-
- @Override
- protected WakeupMessage makeWakeupMessage(
- Context context, Handler handler, String cmdName, int cmd) {
- return new FakeWakeupMessage(context, handler, cmdName, cmd);
- }
}
private class WrappedConnectivityService extends ConnectivityService {
private WrappedNetworkMonitor mLastCreatedNetworkMonitor;
public WrappedConnectivityService(Context context, INetworkManagementService netManager,
- INetworkStatsService statsService, INetworkPolicyManager policyManager) {
- super(context, netManager, statsService, policyManager);
+ INetworkStatsService statsService, INetworkPolicyManager policyManager,
+ IpConnectivityLog log) {
+ super(context, netManager, statsService, policyManager, log);
+ mLingerDelayMs = TEST_LINGER_DELAY_MS;
}
@Override
@@ -624,12 +642,18 @@
@Override
public NetworkMonitor createNetworkMonitor(Context context, Handler handler,
NetworkAgentInfo nai, NetworkRequest defaultRequest) {
- final WrappedNetworkMonitor monitor = new WrappedNetworkMonitor(context, handler, nai,
- defaultRequest);
+ final WrappedNetworkMonitor monitor = new WrappedNetworkMonitor(
+ context, handler, nai, defaultRequest, mock(IpConnectivityLog.class));
mLastCreatedNetworkMonitor = monitor;
return monitor;
}
+ @Override
+ public WakeupMessage makeWakeupMessage(
+ Context context, Handler handler, String cmdName, int cmd, Object obj) {
+ return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj);
+ }
+
public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() {
return mLastCreatedNetworkMonitor;
}
@@ -641,7 +665,6 @@
public void waitForIdle() {
waitForIdle(TIMEOUT_MS);
}
-
}
private interface Criteria {
@@ -675,8 +698,6 @@
public void setUp() throws Exception {
super.setUp();
- NetworkMonitor.SetDefaultLingerTime(120);
-
// InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
// http://b/25897652 .
if (Looper.myLooper() == null) {
@@ -687,21 +708,31 @@
mService = new WrappedConnectivityService(mServiceContext,
mock(INetworkManagementService.class),
mock(INetworkStatsService.class),
- mock(INetworkPolicyManager.class));
+ mock(INetworkPolicyManager.class),
+ mock(IpConnectivityLog.class));
mService.systemReady();
mCm = new WrappedConnectivityManager(getContext(), mService);
mCm.bindProcessToNetwork(null);
}
+ public void tearDown() throws Exception {
+ if (mCellNetworkAgent != null) { mCellNetworkAgent.disconnect(); }
+ if (mWiFiNetworkAgent != null) { mWiFiNetworkAgent.disconnect(); }
+ mCellNetworkAgent = mWiFiNetworkAgent = null;
+ super.tearDown();
+ }
+
private int transportToLegacyType(int transport) {
switch (transport) {
+ case TRANSPORT_ETHERNET:
+ return TYPE_ETHERNET;
case TRANSPORT_WIFI:
return TYPE_WIFI;
case TRANSPORT_CELLULAR:
return TYPE_MOBILE;
default:
- throw new IllegalStateException("Unknown transport" + transport);
+ throw new IllegalStateException("Unknown transport " + transport);
}
}
@@ -911,8 +942,6 @@
mWiFiNetworkAgent.adjustScore(11);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
- mCellNetworkAgent.disconnect();
- mWiFiNetworkAgent.disconnect();
}
@LargeTest
@@ -984,8 +1013,6 @@
assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
verifyActiveNetwork(TRANSPORT_WIFI);
- mCellNetworkAgent.disconnect();
- mWiFiNetworkAgent.disconnect();
}
@LargeTest
@@ -1012,13 +1039,13 @@
assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
verifyActiveNetwork(TRANSPORT_WIFI);
- mCellNetworkAgent.disconnect();
- mWiFiNetworkAgent.disconnect();
}
enum CallbackState {
NONE,
AVAILABLE,
+ NETWORK_CAPABILITIES,
+ LINK_PROPERTIES,
LOSING,
LOST
}
@@ -1029,59 +1056,98 @@
* received. assertNoCallback may be called at any time.
*/
private class TestNetworkCallback extends NetworkCallback {
- private final ConditionVariable mConditionVariable = new ConditionVariable();
- private CallbackState mLastCallback = CallbackState.NONE;
- private Network mLastNetwork;
+ // Chosen to be much less than the linger timeout. This ensures that we can distinguish
+ // between a LOST callback that arrives immediately and a LOST callback that arrives after
+ // the linger timeout.
+ private final static int TIMEOUT_MS = 50;
+ private class CallbackInfo {
+ public final CallbackState state;
+ public final Network network;
+ public Object arg;
+ public CallbackInfo(CallbackState s, Network n, Object o) {
+ state = s; network = n; arg = o;
+ }
+ public String toString() { return String.format("%s (%s)", state, network); }
+ public boolean equals(Object o) {
+ if (!(o instanceof CallbackInfo)) return false;
+ // Ignore timeMs, since it's unpredictable.
+ CallbackInfo other = (CallbackInfo) o;
+ return state == other.state && Objects.equals(network, other.network);
+ }
+ }
+ private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>();
+
+ protected void setLastCallback(CallbackState state, Network network, Object o) {
+ mCallbacks.offer(new CallbackInfo(state, network, o));
+ }
+
+ @Override
public void onAvailable(Network network) {
- assertEquals(CallbackState.NONE, mLastCallback);
- mLastCallback = CallbackState.AVAILABLE;
- mLastNetwork = network;
- mConditionVariable.open();
+ setLastCallback(CallbackState.AVAILABLE, network, null);
}
+ @Override
public void onLosing(Network network, int maxMsToLive) {
- assertEquals(CallbackState.NONE, mLastCallback);
- mLastCallback = CallbackState.LOSING;
- mLastNetwork = network;
- mConditionVariable.open();
+ setLastCallback(CallbackState.LOSING, network, maxMsToLive /* autoboxed int */);
}
+ @Override
public void onLost(Network network) {
- assertEquals(CallbackState.NONE, mLastCallback);
- mLastCallback = CallbackState.LOST;
- mLastNetwork = network;
- mConditionVariable.open();
+ setLastCallback(CallbackState.LOST, network, null);
}
- void expectCallback(CallbackState state) {
- expectCallback(state, null);
+ void expectCallback(CallbackState state, MockNetworkAgent mockAgent, int timeoutMs) {
+ CallbackInfo expected = new CallbackInfo(
+ state, (mockAgent != null) ? mockAgent.getNetwork() : null, 0);
+ CallbackInfo actual;
+ try {
+ actual = mCallbacks.poll(timeoutMs, TimeUnit.MILLISECONDS);
+ assertEquals("Unexpected callback:", expected, actual);
+ } catch (InterruptedException e) {
+ fail("Did not receive expected " + expected + " after " + TIMEOUT_MS + "ms");
+ actual = null; // Or the compiler can't tell it's never used uninitialized.
+ }
+ if (state == CallbackState.LOSING) {
+ String msg = String.format(
+ "Invalid linger time value %d, must be between %d and %d",
+ actual.arg, 0, TEST_LINGER_DELAY_MS);
+ int maxMsToLive = (Integer) actual.arg;
+ assertTrue(msg, 0 <= maxMsToLive && maxMsToLive <= TEST_LINGER_DELAY_MS);
+ }
}
void expectCallback(CallbackState state, MockNetworkAgent mockAgent) {
- waitFor(mConditionVariable);
- assertEquals(state, mLastCallback);
- if (mockAgent != null) {
- assertEquals(mockAgent.getNetwork(), mLastNetwork);
- }
- mLastCallback = CallbackState.NONE;
- mLastNetwork = null;
- mConditionVariable.close();
+ expectCallback(state, mockAgent, TIMEOUT_MS);
}
void assertNoCallback() {
- assertEquals(CallbackState.NONE, mLastCallback);
+ mService.waitForIdle();
+ CallbackInfo c = mCallbacks.peek();
+ assertNull("Unexpected callback: " + c, c);
+ }
+ }
+
+ // Can't be part of TestNetworkCallback because "cannot be declared static; static methods can
+ // only be declared in a static or top level type".
+ static void assertNoCallbacks(TestNetworkCallback ... callbacks) {
+ for (TestNetworkCallback c : callbacks) {
+ c.assertNoCallback();
}
}
@LargeTest
public void testStateChangeNetworkCallbacks() throws Exception {
+ final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback();
final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest genericRequest = new NetworkRequest.Builder()
+ .clearCapabilities().build();
final NetworkRequest wifiRequest = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_WIFI).build();
final NetworkRequest cellRequest = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_CELLULAR).build();
+ mCm.registerNetworkCallback(genericRequest, genericNetworkCallback);
mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
@@ -1089,65 +1155,275 @@
ConditionVariable cv = waitForConnectivityBroadcasts(1);
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false);
- cellNetworkCallback.expectCallback(CallbackState.AVAILABLE);
- wifiNetworkCallback.assertNoCallback();
+ genericNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
waitFor(cv);
+ assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
// This should not trigger spurious onAvailable() callbacks, b/21762680.
mCellNetworkAgent.adjustScore(-1);
mService.waitForIdle();
- wifiNetworkCallback.assertNoCallback();
- cellNetworkCallback.assertNoCallback();
+ assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
cv = waitForConnectivityBroadcasts(2);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- wifiNetworkCallback.expectCallback(CallbackState.AVAILABLE);
- cellNetworkCallback.assertNoCallback();
+ genericNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ wifiNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
waitFor(cv);
+ assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
cv = waitForConnectivityBroadcasts(2);
mWiFiNetworkAgent.disconnect();
- wifiNetworkCallback.expectCallback(CallbackState.LOST);
+ genericNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ wifiNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
cellNetworkCallback.assertNoCallback();
waitFor(cv);
+ assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
cv = waitForConnectivityBroadcasts(1);
mCellNetworkAgent.disconnect();
- cellNetworkCallback.expectCallback(CallbackState.LOST);
- wifiNetworkCallback.assertNoCallback();
+ genericNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
waitFor(cv);
+ assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
// Test validated networks
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- cellNetworkCallback.expectCallback(CallbackState.AVAILABLE);
- wifiNetworkCallback.assertNoCallback();
+ genericNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
// This should not trigger spurious onAvailable() callbacks, b/21762680.
mCellNetworkAgent.adjustScore(-1);
mService.waitForIdle();
- wifiNetworkCallback.assertNoCallback();
- cellNetworkCallback.assertNoCallback();
+ assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- wifiNetworkCallback.expectCallback(CallbackState.AVAILABLE);
- cellNetworkCallback.expectCallback(CallbackState.LOSING);
+ genericNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ wifiNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
+
+ mWiFiNetworkAgent.disconnect();
+ genericNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ wifiNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
+
+ mCellNetworkAgent.disconnect();
+ genericNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
+ }
+
+ @SmallTest
+ public void testMultipleLingering() {
+ NetworkRequest request = new NetworkRequest.Builder()
+ .clearCapabilities().addCapability(NET_CAPABILITY_NOT_METERED)
+ .build();
+ TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, callback);
+
+ TestNetworkCallback defaultCallback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(defaultCallback);
+
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
+
+ mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ mEthernetNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+
+ mCellNetworkAgent.connect(true);
+ callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+ mWiFiNetworkAgent.connect(true);
+ // We get AVAILABLE on wifi when wifi connects and satisfies our unmetered request.
+ // We then get LOSING when wifi validates and cell is outscored.
+ callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+ mEthernetNetworkAgent.connect(true);
+ callback.expectCallback(CallbackState.AVAILABLE, mEthernetNetworkAgent);
+ callback.expectCallback(CallbackState.LOSING, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mEthernetNetworkAgent);
+ assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+ mEthernetNetworkAgent.disconnect();
+ callback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+
+ for (int i = 0; i < 4; i++) {
+ MockNetworkAgent oldNetwork, newNetwork;
+ if (i % 2 == 0) {
+ mWiFiNetworkAgent.adjustScore(-15);
+ oldNetwork = mWiFiNetworkAgent;
+ newNetwork = mCellNetworkAgent;
+ } else {
+ mWiFiNetworkAgent.adjustScore(15);
+ oldNetwork = mCellNetworkAgent;
+ newNetwork = mWiFiNetworkAgent;
+
+ }
+ callback.expectCallback(CallbackState.LOSING, oldNetwork);
+ // TODO: should we send an AVAILABLE callback to newNetwork, to indicate that it is no
+ // longer lingering?
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, newNetwork);
+ assertEquals(newNetwork.getNetwork(), mCm.getActiveNetwork());
+ }
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+ // Verify that if a network no longer satisfies a request, we send LOST and not LOSING, even
+ // if the network is still up.
+ mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
+ callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+
+ // Wifi no longer satisfies our listen, which is for an unmetered network.
+ // But because its score is 55, it's still up (and the default network).
+ defaultCallback.assertNoCallback();
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+ // Disconnect our test networks.
+ mWiFiNetworkAgent.disconnect();
+ defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ mCellNetworkAgent.disconnect();
+ defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+
+ mCm.unregisterNetworkCallback(callback);
+ mService.waitForIdle();
+
+ // Check that a network is only lingered or torn down if it would not satisfy a request even
+ // if it validated.
+ request = new NetworkRequest.Builder().clearCapabilities().build();
+ callback = new TestNetworkCallback();
+
+ mCm.registerNetworkCallback(request, callback);
+
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(false); // Score: 10
+ callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+ // Bring up wifi with a score of 20.
+ // Cell stays up because it would satisfy the default request if it validated.
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false); // Score: 20
+ callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect();
- wifiNetworkCallback.expectCallback(CallbackState.LOST);
- cellNetworkCallback.assertNoCallback();
+ callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ // Bring up wifi with a score of 70.
+ // Cell is lingered because it would not satisfy any request, even if it validated.
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.adjustScore(50);
+ mWiFiNetworkAgent.connect(false); // Score: 70
+ callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+ // Tear down wifi.
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+ // Bring up wifi, then validate it. Previous versions would immediately tear down cell, but
+ // it's arguably correct to linger it, since it was the default network before it validated.
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
mCellNetworkAgent.disconnect();
- cellNetworkCallback.expectCallback(CallbackState.LOST);
- wifiNetworkCallback.assertNoCallback();
+ callback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+
+ // If a network is lingering, and we add and remove a request from it, resume lingering.
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+
+ NetworkRequest cellRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build();
+ NetworkCallback noopCallback = new NetworkCallback();
+ mCm.requestNetwork(cellRequest, noopCallback);
+ // TODO: should this cause an AVAILABLE callback, to indicate that the network is no longer
+ // lingering?
+ mCm.unregisterNetworkCallback(noopCallback);
+ callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+
+ // Similar to the above: lingering can start even after the lingered request is removed.
+ // Disconnect wifi and switch to cell.
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+
+ // Cell is now the default network. Pin it with a cell-specific request.
+ noopCallback = new NetworkCallback(); // Can't reuse NetworkCallbacks. http://b/20701525
+ mCm.requestNetwork(cellRequest, noopCallback);
+
+ // Now connect wifi, and expect it to become the default network.
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ // The default request is lingering on cell, but nothing happens to cell, and we send no
+ // callbacks for it, because it's kept up by cellRequest.
+ callback.assertNoCallback();
+ // Now unregister cellRequest and expect cell to start lingering.
+ mCm.unregisterNetworkCallback(noopCallback);
+ callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+
+ // Let linger run its course.
+ callback.assertNoCallback();
+ callback.expectCallback(CallbackState.LOST, mCellNetworkAgent,
+ TEST_LINGER_DELAY_MS /* timeoutMs */);
+
+ // Clean up.
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+
+ mCm.unregisterNetworkCallback(callback);
+ mCm.unregisterNetworkCallback(defaultCallback);
}
private void tryNetworkFactoryRequests(int capability) throws Exception {
@@ -1314,7 +1590,7 @@
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS);
mCellNetworkAgent.connectWithoutInternet();
- networkCallback.expectCallback(CallbackState.AVAILABLE);
+ networkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
verifyActiveNetwork(TRANSPORT_WIFI);
// Test releasing NetworkRequest disconnects cellular with MMS
cv = mCellNetworkAgent.getDisconnectedCV();
@@ -1340,7 +1616,7 @@
MockNetworkAgent mmsNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mmsNetworkAgent.addCapability(NET_CAPABILITY_MMS);
mmsNetworkAgent.connectWithoutInternet();
- networkCallback.expectCallback(CallbackState.AVAILABLE);
+ networkCallback.expectCallback(CallbackState.AVAILABLE, mmsNetworkAgent);
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test releasing MMS NetworkRequest does not disconnect main cellular NetworkAgent
cv = mmsNetworkAgent.getDisconnectedCV();
@@ -1366,36 +1642,36 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
String firstRedirectUrl = "http://example.com/firstPath";
mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
- captivePortalCallback.expectCallback(CallbackState.AVAILABLE);
+ captivePortalCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl);
// Take down network.
// Expect onLost callback.
mWiFiNetworkAgent.disconnect();
- captivePortalCallback.expectCallback(CallbackState.LOST);
+ captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
// Bring up a network with a captive portal.
// Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
String secondRedirectUrl = "http://example.com/secondPath";
mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
- captivePortalCallback.expectCallback(CallbackState.AVAILABLE);
+ captivePortalCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), secondRedirectUrl);
// Make captive portal disappear then revalidate.
// Expect onLost callback because network no longer provides NET_CAPABILITY_CAPTIVE_PORTAL.
mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 204;
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
- captivePortalCallback.expectCallback(CallbackState.LOST);
+ captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
// Expect NET_CAPABILITY_VALIDATED onAvailable callback.
- validatedCallback.expectCallback(CallbackState.AVAILABLE);
+ validatedCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
// Break network connectivity.
// Expect NET_CAPABILITY_VALIDATED onLost callback.
mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 500;
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
- validatedCallback.expectCallback(CallbackState.LOST);
+ validatedCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
}
@SmallTest
@@ -1410,7 +1686,7 @@
// do nothing - should get here
}
- assertTrue("NetworkReqeuest builder with MATCH_ALL_REQUESTS_NETWORK_SPECIFIER",
+ assertTrue("NetworkRequest builder with MATCH_ALL_REQUESTS_NETWORK_SPECIFIER",
execptionCalled);
try {
@@ -1477,6 +1753,206 @@
defaultNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
}
+ private class TestRequestUpdateCallback extends TestNetworkCallback {
+ @Override
+ public void onCapabilitiesChanged(Network network, NetworkCapabilities netCap) {
+ setLastCallback(CallbackState.NETWORK_CAPABILITIES, network, netCap);
+ }
+
+ @Override
+ public void onLinkPropertiesChanged(Network network, LinkProperties linkProp) {
+ setLastCallback(CallbackState.LINK_PROPERTIES, network, linkProp);
+ }
+ }
+
+ @LargeTest
+ public void testRequestCallbackUpdates() throws Exception {
+ // File a network request for mobile.
+ final TestNetworkCallback cellNetworkCallback = new TestRequestUpdateCallback();
+ final NetworkRequest cellRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build();
+ mCm.requestNetwork(cellRequest, cellNetworkCallback);
+
+ // Bring up the mobile network.
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+
+ // We should get onAvailable().
+ cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ // We should get onCapabilitiesChanged(), when the mobile network successfully validates.
+ cellNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+
+ // Update LinkProperties.
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName("foonet_data0");
+ mCellNetworkAgent.sendLinkProperties(lp);
+ // We should get onLinkPropertiesChanged().
+ cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+
+ // Register a garden variety default network request.
+ final TestNetworkCallback dfltNetworkCallback = new TestRequestUpdateCallback();
+ mCm.registerDefaultNetworkCallback(dfltNetworkCallback);
+ // Only onAvailable() is called; no other information is delivered.
+ dfltNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ dfltNetworkCallback.assertNoCallback();
+
+ // Request a NetworkCapabilities update; only the requesting callback is notified.
+ mCm.requestNetworkCapabilities(dfltNetworkCallback);
+ dfltNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+ dfltNetworkCallback.assertNoCallback();
+
+ // Request a LinkProperties update; only the requesting callback is notified.
+ mCm.requestLinkProperties(dfltNetworkCallback);
+ dfltNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+ dfltNetworkCallback.assertNoCallback();
+
+ mCm.unregisterNetworkCallback(dfltNetworkCallback);
+ mCm.unregisterNetworkCallback(cellNetworkCallback);
+ }
+
+ @SmallTest
+ public void testRequestBenchmark() throws Exception {
+ // Benchmarks connecting and switching performance in the presence of a large number of
+ // NetworkRequests.
+ // 1. File NUM_REQUESTS requests.
+ // 2. Have a network connect. Wait for NUM_REQUESTS onAvailable callbacks to fire.
+ // 3. Have a new network connect and outscore the previous. Wait for NUM_REQUESTS onLosing
+ // and NUM_REQUESTS onAvailable callbacks to fire.
+ // See how long it took.
+ final int NUM_REQUESTS = 90;
+ final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
+ final NetworkCallback[] callbacks = new NetworkCallback[NUM_REQUESTS];
+ final CountDownLatch availableLatch = new CountDownLatch(NUM_REQUESTS);
+ final CountDownLatch losingLatch = new CountDownLatch(NUM_REQUESTS);
+
+ final int REGISTER_TIME_LIMIT_MS = 100;
+ long startTime = System.currentTimeMillis();
+ for (int i = 0; i < NUM_REQUESTS; i++) {
+ callbacks[i] = new NetworkCallback() {
+ @Override public void onAvailable(Network n) { availableLatch.countDown(); }
+ @Override public void onLosing(Network n, int t) { losingLatch.countDown(); }
+ };
+ mCm.registerNetworkCallback(request, callbacks[i]);
+ }
+ long timeTaken = System.currentTimeMillis() - startTime;
+ String msg = String.format("Register %d callbacks: %dms, acceptable %dms",
+ NUM_REQUESTS, timeTaken, REGISTER_TIME_LIMIT_MS);
+ Log.d(TAG, msg);
+ assertTrue(msg, timeTaken < REGISTER_TIME_LIMIT_MS);
+
+ final int CONNECT_TIME_LIMIT_MS = 30;
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ // Don't request that the network validate, because otherwise connect() will block until
+ // the network gets NET_CAPABILITY_VALIDATED, after all the callbacks below have fired,
+ // and we won't actually measure anything.
+ mCellNetworkAgent.connect(false);
+ startTime = System.currentTimeMillis();
+ if (!availableLatch.await(CONNECT_TIME_LIMIT_MS, TimeUnit.MILLISECONDS)) {
+ fail(String.format("Only dispatched %d/%d onAvailable callbacks in %dms",
+ NUM_REQUESTS - availableLatch.getCount(), NUM_REQUESTS,
+ CONNECT_TIME_LIMIT_MS));
+ }
+ timeTaken = System.currentTimeMillis() - startTime;
+ Log.d(TAG, String.format("Connect, %d callbacks: %dms, acceptable %dms",
+ NUM_REQUESTS, timeTaken, CONNECT_TIME_LIMIT_MS));
+
+ final int SWITCH_TIME_LIMIT_MS = 30;
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ // Give wifi a high enough score that we'll linger cell when wifi comes up.
+ mWiFiNetworkAgent.adjustScore(40);
+ mWiFiNetworkAgent.connect(false);
+ startTime = System.currentTimeMillis();
+ if (!losingLatch.await(SWITCH_TIME_LIMIT_MS, TimeUnit.MILLISECONDS)) {
+ fail(String.format("Only dispatched %d/%d onLosing callbacks in %dms",
+ NUM_REQUESTS - losingLatch.getCount(), NUM_REQUESTS, SWITCH_TIME_LIMIT_MS));
+ }
+ timeTaken = System.currentTimeMillis() - startTime;
+ Log.d(TAG, String.format("Linger, %d callbacks: %dms, acceptable %dms",
+ NUM_REQUESTS, timeTaken, SWITCH_TIME_LIMIT_MS));
+
+ final int UNREGISTER_TIME_LIMIT_MS = 10;
+ startTime = System.currentTimeMillis();
+ for (int i = 0; i < NUM_REQUESTS; i++) {
+ mCm.unregisterNetworkCallback(callbacks[i]);
+ }
+ timeTaken = System.currentTimeMillis() - startTime;
+ msg = String.format("Unregister %d callbacks: %dms, acceptable %dms",
+ NUM_REQUESTS, timeTaken, UNREGISTER_TIME_LIMIT_MS);
+ Log.d(TAG, msg);
+ assertTrue(msg, timeTaken < UNREGISTER_TIME_LIMIT_MS);
+ }
+
+ @SmallTest
+ public void testMobileDataAlwaysOn() throws Exception {
+ final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest cellRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build();
+ mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+
+ final HandlerThread handlerThread = new HandlerThread("MobileDataAlwaysOnFactory");
+ handlerThread.start();
+ NetworkCapabilities filter = new NetworkCapabilities()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_INTERNET);
+ final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
+ mServiceContext, "testFactory", filter);
+ testFactory.setScoreFilter(40);
+
+ // Register the factory and expect it to start looking for a network.
+ testFactory.expectAddRequests(1);
+ testFactory.register();
+ testFactory.waitForNetworkRequests(1);
+ assertTrue(testFactory.getMyStartRequested());
+
+ // Bring up wifi. The factory stops looking for a network.
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ testFactory.expectAddRequests(2); // Because the default request changes score twice.
+ mWiFiNetworkAgent.connect(true);
+ testFactory.waitForNetworkRequests(1);
+ assertFalse(testFactory.getMyStartRequested());
+
+ ContentResolver cr = mServiceContext.getContentResolver();
+
+ // Turn on mobile data always on. The factory starts looking again.
+ testFactory.expectAddRequests(1);
+ Settings.Global.putInt(cr, Settings.Global.MOBILE_DATA_ALWAYS_ON, 1);
+ mService.updateMobileDataAlwaysOn();
+ testFactory.waitForNetworkRequests(2);
+ assertTrue(testFactory.getMyStartRequested());
+
+ // Bring up cell data and check that the factory stops looking.
+ assertEquals(1, mCm.getAllNetworks().length);
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ testFactory.expectAddRequests(2); // Because the cell request changes score twice.
+ mCellNetworkAgent.connect(true);
+ cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ testFactory.waitForNetworkRequests(2);
+ assertFalse(testFactory.getMyStartRequested()); // Because the cell network outscores us.
+
+ // Check that cell data stays up.
+ mService.waitForIdle();
+ verifyActiveNetwork(TRANSPORT_WIFI);
+ assertEquals(2, mCm.getAllNetworks().length);
+
+ // Turn off mobile data always on and expect the request to disappear...
+ testFactory.expectRemoveRequests(1);
+ Settings.Global.putInt(cr, Settings.Global.MOBILE_DATA_ALWAYS_ON, 0);
+ mService.updateMobileDataAlwaysOn();
+ testFactory.waitForNetworkRequests(1);
+
+ // ... and cell data to be torn down.
+ cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ assertEquals(1, mCm.getAllNetworks().length);
+
+ testFactory.unregister();
+ mCm.unregisterNetworkCallback(cellNetworkCallback);
+ handlerThread.quit();
+ }
+
private static class TestKeepaliveCallback extends PacketKeepaliveCallback {
public static enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR };
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 622e46e..541be3d 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -20,6 +20,7 @@
import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.NetworkPolicy.CYCLE_NONE;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.WARNING_DISABLED;
import static android.net.NetworkPolicyManager.POLICY_NONE;
@@ -68,7 +69,6 @@
import android.net.NetworkTemplate;
import android.os.Binder;
import android.os.INetworkManagementService;
-import android.os.IPowerManager;
import android.os.MessageQueue.IdleHandler;
import android.os.UserHandle;
import android.test.AndroidTestCase;
@@ -86,7 +86,9 @@
import org.easymock.IAnswer;
import java.io.File;
+import java.util.Calendar;
import java.util.LinkedHashSet;
+import java.util.TimeZone;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
@@ -112,7 +114,6 @@
private File mPolicyDir;
private IActivityManager mActivityManager;
- private IPowerManager mPowerManager;
private INetworkStatsService mStatsService;
private INetworkManagementService mNetworkManager;
private INetworkPolicyListener mPolicyListener;
@@ -141,8 +142,7 @@
private static final int PID_2 = 401;
private static final int PID_3 = 402;
- @Override
- public void setUp() throws Exception {
+ public void _setUp() throws Exception {
super.setUp();
setCurrentTimeMillis(TEST_START);
@@ -185,7 +185,6 @@
}
mActivityManager = createMock(IActivityManager.class);
- mPowerManager = createMock(IPowerManager.class);
mStatsService = createMock(INetworkStatsService.class);
mNetworkManager = createMock(INetworkManagementService.class);
mPolicyListener = createMock(INetworkPolicyListener.class);
@@ -193,7 +192,7 @@
mConnManager = createMock(IConnectivityManager.class);
mNotifManager = createMock(INotificationManager.class);
- mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager, mPowerManager,
+ mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager,
mStatsService, mNetworkManager, mTime, mPolicyDir, true);
mService.bindConnectivityManager(mConnManager);
mService.bindNotificationManager(mNotifManager);
@@ -215,8 +214,6 @@
mNetworkManager.registerObserver(capture(networkObserver));
expectLastCall().atLeastOnce();
- // expect to answer screen status during systemReady()
- expect(mPowerManager.isInteractive()).andReturn(true).atLeastOnce();
expect(mNetworkManager.isBandwidthControlEnabled()).andReturn(true).atLeastOnce();
expectCurrentTime();
@@ -229,8 +226,7 @@
}
- @Override
- public void tearDown() throws Exception {
+ public void _tearDown() throws Exception {
for (File file : mPolicyDir.listFiles()) {
file.delete();
}
@@ -239,7 +235,6 @@
mPolicyDir = null;
mActivityManager = null;
- mPowerManager = null;
mStatsService = null;
mPolicyListener = null;
mTime = null;
@@ -263,6 +258,7 @@
backgroundChanged.get();
}
+ @Suppress
public void testPidForegroundCombined() throws Exception {
IdleFuture idle;
@@ -310,47 +306,7 @@
assertFalse(mService.isUidForeground(UID_A));
}
- public void testScreenChangesRules() throws Exception {
- Future<Void> future;
-
- expectSetUidMeteredNetworkBlacklist(UID_A, false);
- expectSetUidForeground(UID_A, true);
- future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
- replay();
- mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
- future.get();
- verifyAndReset();
-
- // push strict policy for foreground uid, verify ALLOW rule
- expectSetUidMeteredNetworkBlacklist(UID_A, false);
- expectSetUidForeground(UID_A, true);
- future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
- replay();
- mService.setUidPolicy(APP_ID_A, POLICY_REJECT_METERED_BACKGROUND);
- future.get();
- verifyAndReset();
-
- // now turn screen off and verify REJECT rule
- expect(mPowerManager.isInteractive()).andReturn(false).atLeastOnce();
- expectSetUidMeteredNetworkBlacklist(UID_A, true);
- expectSetUidForeground(UID_A, false);
- future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
- replay();
- mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_OFF));
- future.get();
- verifyAndReset();
-
- // and turn screen back on, verify ALLOW rule restored
- expect(mPowerManager.isInteractive()).andReturn(true).atLeastOnce();
- expectSetUidMeteredNetworkBlacklist(UID_A, false);
- expectSetUidForeground(UID_A, true);
- future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
- replay();
- mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_ON));
- future.get();
- verifyAndReset();
- }
-
+ @Suppress
public void testPolicyNone() throws Exception {
Future<Void> future;
@@ -381,6 +337,7 @@
verifyAndReset();
}
+ @Suppress
public void testPolicyReject() throws Exception {
Future<Void> future;
@@ -412,6 +369,7 @@
verifyAndReset();
}
+ @Suppress
public void testPolicyRejectAddRemove() throws Exception {
Future<Void> future;
@@ -576,6 +534,17 @@
computeLastCycleBoundary(parseTime("2013-01-14T15:11:00.000-08:00"), policy));
}
+ public void testLastCycleBoundaryDST() throws Exception {
+ final long currentTime = parseTime("1989-01-02T07:30:00.000");
+ final long expectedCycle = parseTime("1988-12-03T02:00:00.000Z");
+
+ final NetworkPolicy policy = new NetworkPolicy(
+ sTemplateWifi, 3, "America/Argentina/Buenos_Aires", 1024L, 1024L, false);
+ final long actualCycle = computeLastCycleBoundary(currentTime, policy);
+ assertTimeEquals(expectedCycle, actualCycle);
+ }
+
+ @Suppress
public void testNetworkPolicyAppliedCycleLastMonth() throws Exception {
NetworkState[] state = null;
NetworkStats stats = null;
@@ -628,6 +597,7 @@
verifyAndReset();
}
+ @Suppress
public void testUidRemovedPolicyCleared() throws Exception {
Future<Void> future;
@@ -652,6 +622,7 @@
verifyAndReset();
}
+ @Suppress
public void testOverWarningLimitNotification() throws Exception {
NetworkState[] state = null;
NetworkStats stats = null;
@@ -783,6 +754,7 @@
}
}
+ @Suppress
public void testMeteredNetworkWithoutLimit() throws Exception {
NetworkState[] state = null;
NetworkStats stats = null;
@@ -1029,14 +1001,14 @@
}
private void replay() {
- EasyMock.replay(mActivityManager, mPowerManager, mStatsService, mPolicyListener,
- mNetworkManager, mTime, mConnManager, mNotifManager);
+ EasyMock.replay(mActivityManager, mStatsService, mPolicyListener, mNetworkManager, mTime,
+ mConnManager, mNotifManager);
}
private void verifyAndReset() {
- EasyMock.verify(mActivityManager, mPowerManager, mStatsService, mPolicyListener,
- mNetworkManager, mTime, mConnManager, mNotifManager);
- EasyMock.reset(mActivityManager, mPowerManager, mStatsService, mPolicyListener,
- mNetworkManager, mTime, mConnManager, mNotifManager);
+ EasyMock.verify(mActivityManager, mStatsService, mPolicyListener, mNetworkManager, mTime,
+ mConnManager, mNotifManager);
+ EasyMock.reset(mActivityManager, mStatsService, mPolicyListener, mNetworkManager, mTime,
+ mConnManager, mNotifManager);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index ef9739d..404c142 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -337,6 +337,10 @@
@Override
public void invalidateCache(int userId) {
}
+
+ @Override
+ public void updateServices(int userId) {
+ }
}
static public class MyMockContext extends MockContext {
diff --git a/services/tests/servicestests/src/com/android/server/connectivity/DnsEventListenerServiceTest.java b/services/tests/servicestests/src/com/android/server/connectivity/DnsEventListenerServiceTest.java
new file mode 100644
index 0000000..033b2c9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/connectivity/DnsEventListenerServiceTest.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.metrics.DnsEvent;
+import android.net.metrics.IDnsEventListener;
+import android.net.metrics.IpConnectivityLog;
+
+import junit.framework.TestCase;
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.List;
+import java.util.OptionalInt;
+import java.util.stream.IntStream;
+
+public class DnsEventListenerServiceTest extends TestCase {
+
+ // TODO: read from DnsEventListenerService after this constant is read from system property
+ static final int BATCH_SIZE = 100;
+ static final int EVENT_TYPE = IDnsEventListener.EVENT_GETADDRINFO;
+ // TODO: read from IDnsEventListener
+ static final int RETURN_CODE = 1;
+
+ static final byte[] EVENT_TYPES = new byte[BATCH_SIZE];
+ static final byte[] RETURN_CODES = new byte[BATCH_SIZE];
+ static final int[] LATENCIES = new int[BATCH_SIZE];
+ static {
+ for (int i = 0; i < BATCH_SIZE; i++) {
+ EVENT_TYPES[i] = EVENT_TYPE;
+ RETURN_CODES[i] = RETURN_CODE;
+ LATENCIES[i] = i;
+ }
+ }
+
+ DnsEventListenerService mDnsService;
+
+ @Mock ConnectivityManager mCm;
+ @Mock IpConnectivityLog mLog;
+ ArgumentCaptor<NetworkCallback> mCallbackCaptor;
+ ArgumentCaptor<DnsEvent> mEvCaptor;
+
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mCallbackCaptor = ArgumentCaptor.forClass(NetworkCallback.class);
+ mEvCaptor = ArgumentCaptor.forClass(DnsEvent.class);
+ mDnsService = new DnsEventListenerService(mCm, mLog);
+
+ verify(mCm, times(1)).registerNetworkCallback(any(), mCallbackCaptor.capture());
+ }
+
+ public void testOneBatch() throws Exception {
+ log(105, LATENCIES);
+ log(106, Arrays.copyOf(LATENCIES, BATCH_SIZE - 1)); // one lookup short of a batch event
+
+ verifyLoggedEvents(new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES));
+
+ log(106, Arrays.copyOfRange(LATENCIES, BATCH_SIZE - 1, BATCH_SIZE));
+
+ mEvCaptor = ArgumentCaptor.forClass(DnsEvent.class); // reset argument captor
+ verifyLoggedEvents(
+ new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
+ new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES));
+ }
+
+ public void testSeveralBatches() throws Exception {
+ log(105, LATENCIES);
+ log(106, LATENCIES);
+ log(105, LATENCIES);
+ log(107, LATENCIES);
+
+ verifyLoggedEvents(
+ new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
+ new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES),
+ new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
+ new DnsEvent(107, EVENT_TYPES, RETURN_CODES, LATENCIES));
+ }
+
+ public void testBatchAndNetworkLost() throws Exception {
+ byte[] eventTypes = Arrays.copyOf(EVENT_TYPES, 20);
+ byte[] returnCodes = Arrays.copyOf(RETURN_CODES, 20);
+ int[] latencies = Arrays.copyOf(LATENCIES, 20);
+
+ log(105, LATENCIES);
+ log(105, latencies);
+ mCallbackCaptor.getValue().onLost(new Network(105));
+ log(105, LATENCIES);
+
+ verifyLoggedEvents(
+ new DnsEvent(105, eventTypes, returnCodes, latencies),
+ new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
+ new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES));
+ }
+
+ public void testConcurrentBatchesAndDumps() throws Exception {
+ final long stop = System.currentTimeMillis() + 100;
+ final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
+ new Thread() {
+ public void run() {
+ while (System.currentTimeMillis() < stop) {
+ mDnsService.dump(pw);
+ }
+ }
+ }.start();
+
+ logAsync(105, LATENCIES);
+ logAsync(106, LATENCIES);
+ logAsync(107, LATENCIES);
+
+ verifyLoggedEvents(500,
+ new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
+ new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES),
+ new DnsEvent(107, EVENT_TYPES, RETURN_CODES, LATENCIES));
+ }
+
+ public void testConcurrentBatchesAndNetworkLoss() throws Exception {
+ logAsync(105, LATENCIES);
+ Thread.sleep(10L);
+ // call onLost() asynchronously to logAsync's onDnsEvent() calls.
+ mCallbackCaptor.getValue().onLost(new Network(105));
+
+ // do not verify unpredictable batch
+ verify(mLog, timeout(500).times(1)).log(any());
+ }
+
+ void log(int netId, int[] latencies) {
+ for (int l : latencies) {
+ mDnsService.onDnsEvent(netId, EVENT_TYPE, RETURN_CODE, l);
+ }
+ }
+
+ void logAsync(int netId, int[] latencies) {
+ new Thread() {
+ public void run() {
+ log(netId, latencies);
+ }
+ }.start();
+ }
+
+ void verifyLoggedEvents(DnsEvent... expected) {
+ verifyLoggedEvents(0, expected);
+ }
+
+ void verifyLoggedEvents(int wait, DnsEvent... expectedEvents) {
+ verify(mLog, timeout(wait).times(expectedEvents.length)).log(mEvCaptor.capture());
+ for (DnsEvent got : mEvCaptor.getAllValues()) {
+ OptionalInt index = IntStream.range(0, expectedEvents.length)
+ .filter(i -> eventsEqual(expectedEvents[i], got))
+ .findFirst();
+ // Don't match same expected event more than once.
+ index.ifPresent(i -> expectedEvents[i] = null);
+ assertTrue(index.isPresent());
+ }
+ }
+
+ /** equality function for DnsEvent to avoid overriding equals() and hashCode(). */
+ static boolean eventsEqual(DnsEvent expected, DnsEvent got) {
+ return (expected == got) || ((expected != null) && (got != null)
+ && (expected.netId == got.netId)
+ && Arrays.equals(expected.eventTypes, got.eventTypes)
+ && Arrays.equals(expected.returnCodes, got.returnCodes)
+ && Arrays.equals(expected.latenciesMs, got.latenciesMs));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/connectivity/MetricsLoggerServiceTest.java b/services/tests/servicestests/src/com/android/server/connectivity/MetricsLoggerServiceTest.java
new file mode 100644
index 0000000..5f84ea1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/connectivity/MetricsLoggerServiceTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.content.Context;
+import android.net.ConnectivityMetricsEvent;
+import android.os.Bundle;
+import android.os.RemoteException;
+import static android.net.ConnectivityMetricsEvent.Reference;
+
+import junit.framework.TestCase;
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertArrayEquals;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/*
+ * TODO:
+ * - allow overriding MetricsLoggerService constants in tests.
+ * - test intents are correctly sent after the notification threshold.
+ * - test oldest events are correctly pushed out when internal deque is full.
+ * - test throttling triggers correctly.
+ */
+public class MetricsLoggerServiceTest extends TestCase {
+
+ static final int COMPONENT_TAG = 1;
+ static final long N_EVENTS = 10L;
+ static final ConnectivityMetricsEvent EVENTS[] = new ConnectivityMetricsEvent[(int)N_EVENTS];
+ static {
+ for (int i = 0; i < N_EVENTS; i++) {
+ EVENTS[i] = new ConnectivityMetricsEvent(i, COMPONENT_TAG, i, new Bundle());
+ }
+ }
+
+ static final ConnectivityMetricsEvent NO_EVENTS[] = new ConnectivityMetricsEvent[0];
+
+ @Mock Context mContext;
+ MetricsLoggerService mService;
+
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mService = new MetricsLoggerService(mContext);
+ mService.onStart();
+ }
+
+ public void testGetNoEvents() throws Exception {
+ Reference r = new Reference(0);
+ assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r));
+ assertEquals(0, r.getValue());
+ }
+
+ public void testLogAndGetEvents() throws Exception {
+ mService.mBinder.logEvents(EVENTS);
+
+ Reference r = new Reference(0);
+
+ assertArrayEquals(EVENTS, mService.mBinder.getEvents(r));
+ assertEquals(N_EVENTS, r.getValue());
+
+ assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r));
+ assertEquals(N_EVENTS, r.getValue());
+ }
+
+ public void testLogOneByOne() throws Exception {
+ for (ConnectivityMetricsEvent ev : EVENTS) {
+ mService.mBinder.logEvent(ev);
+ }
+
+ Reference r = new Reference(0);
+
+ assertArrayEquals(EVENTS, mService.mBinder.getEvents(r));
+ assertEquals(N_EVENTS, r.getValue());
+
+ assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r));
+ assertEquals(N_EVENTS, r.getValue());
+ }
+
+ public void testInterleavedLogAndGet() throws Exception {
+ mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 0, 3));
+
+ Reference r = new Reference(0);
+
+ assertArrayEquals(Arrays.copyOfRange(EVENTS, 0, 3), mService.mBinder.getEvents(r));
+ assertEquals(3, r.getValue());
+
+ mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 3, 8));
+ mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 8, 10));
+
+ assertArrayEquals(Arrays.copyOfRange(EVENTS, 3, 10), mService.mBinder.getEvents(r));
+ assertEquals(N_EVENTS, r.getValue());
+
+ assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r));
+ assertEquals(N_EVENTS, r.getValue());
+ }
+
+ public void testMultipleGetAll() throws Exception {
+ mService.mBinder.logEvents(Arrays.copyOf(EVENTS, 3));
+
+ Reference r1 = new Reference(0);
+ assertArrayEquals(Arrays.copyOf(EVENTS, 3), mService.mBinder.getEvents(r1));
+ assertEquals(3, r1.getValue());
+
+ mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 3, 10));
+
+ Reference r2 = new Reference(0);
+ assertArrayEquals(EVENTS, mService.mBinder.getEvents(r2));
+ assertEquals(N_EVENTS, r2.getValue());
+ }
+
+ public void testLogAndDumpConcurrently() throws Exception {
+ for (int i = 0; i < 50; i++) {
+ mContext = null;
+ mService = null;
+ setUp();
+ logAndDumpConcurrently();
+ }
+ }
+
+ public void logAndDumpConcurrently() throws Exception {
+ final CountDownLatch latch = new CountDownLatch((int)N_EVENTS);
+ final FileDescriptor fd = new FileOutputStream("/dev/null").getFD();
+
+ for (ConnectivityMetricsEvent ev : EVENTS) {
+ new Thread() {
+ public void run() {
+ mService.mBinder.logEvent(ev);
+ latch.countDown();
+ }
+ }.start();
+ }
+
+ new Thread() {
+ public void run() {
+ while (latch.getCount() > 0) {
+ mService.mBinder.dump(fd, new String[]{"--all"});
+ }
+ }
+ }.start();
+
+ latch.await(100, TimeUnit.MILLISECONDS);
+
+ Reference r = new Reference(0);
+ ConnectivityMetricsEvent[] got = mService.mBinder.getEvents(r);
+ Arrays.sort(got, new EventComparator());
+ assertArrayEquals(EVENTS, got);
+ assertEquals(N_EVENTS, r.getValue());
+ }
+
+ static class EventComparator implements Comparator<ConnectivityMetricsEvent> {
+ public int compare(ConnectivityMetricsEvent ev1, ConnectivityMetricsEvent ev2) {
+ return Long.compare(ev1.timestamp, ev2.timestamp);
+ }
+ public boolean equal(Object o) {
+ return o instanceof EventComparator;
+ }
+ };
+}
diff --git a/services/tests/servicestests/src/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/services/tests/servicestests/src/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
new file mode 100644
index 0000000..a30b362
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
+import static com.android.server.connectivity.tethering.IControlsTethering.STATE_AVAILABLE;
+import static com.android.server.connectivity.tethering.IControlsTethering.STATE_TETHERED;
+import static com.android.server.connectivity.tethering.IControlsTethering.STATE_UNAVAILABLE;
+
+import android.net.ConnectivityManager;
+import android.net.INetworkStatsService;
+import android.net.InterfaceConfiguration;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TetherInterfaceStateMachineTest {
+ private static final String IFACE_NAME = "testnet1";
+ private static final String UPSTREAM_IFACE = "upstream0";
+ private static final String UPSTREAM_IFACE2 = "upstream1";
+
+ @Mock private INetworkManagementService mNMService;
+ @Mock private INetworkStatsService mStatsService;
+ @Mock private IControlsTethering mTetherHelper;
+ @Mock private InterfaceConfiguration mInterfaceConfiguration;
+
+ private final TestLooper mLooper = new TestLooper();
+ private TetherInterfaceStateMachine mTestedSm;
+
+ private void initStateMachine(int interfaceType) throws Exception {
+ mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(), interfaceType,
+ mNMService, mStatsService, mTetherHelper);
+ mTestedSm.start();
+ // Starting the state machine always puts us in a consistent state and notifies
+ // the test of the world that we've changed from an unknown to available state.
+ mLooper.dispatchAll();
+ reset(mNMService, mStatsService, mTetherHelper);
+ when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
+ }
+
+ private void initTetheredStateMachine(int interfaceType, String upstreamIface) throws Exception {
+ initStateMachine(interfaceType);
+ dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED);
+ if (upstreamIface != null) {
+ dispatchTetherConnectionChanged(upstreamIface);
+ }
+ reset(mNMService, mStatsService, mTetherHelper);
+ when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
+ }
+
+ @Before public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void startsOutAvailable() {
+ mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(),
+ ConnectivityManager.TETHERING_BLUETOOTH, mNMService, mStatsService, mTetherHelper);
+ mTestedSm.start();
+ mLooper.dispatchAll();
+ verify(mTetherHelper).notifyInterfaceStateChange(
+ IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
+ verifyNoMoreInteractions(mTetherHelper, mNMService, mStatsService);
+ }
+
+ @Test
+ public void shouldDoNothingUntilRequested() throws Exception {
+ initStateMachine(ConnectivityManager.TETHERING_BLUETOOTH);
+ final int [] NOOP_COMMANDS = {
+ TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED,
+ TetherInterfaceStateMachine.CMD_IP_FORWARDING_ENABLE_ERROR,
+ TetherInterfaceStateMachine.CMD_IP_FORWARDING_DISABLE_ERROR,
+ TetherInterfaceStateMachine.CMD_START_TETHERING_ERROR,
+ TetherInterfaceStateMachine.CMD_STOP_TETHERING_ERROR,
+ TetherInterfaceStateMachine.CMD_SET_DNS_FORWARDERS_ERROR,
+ TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED
+ };
+ for (int command : NOOP_COMMANDS) {
+ // None of these commands should trigger us to request action from
+ // the rest of the system.
+ dispatchCommand(command);
+ verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ }
+ }
+
+ @Test
+ public void handlesImmediateInterfaceDown() throws Exception {
+ initStateMachine(ConnectivityManager.TETHERING_BLUETOOTH);
+
+ dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
+ verify(mTetherHelper).notifyInterfaceStateChange(
+ IFACE_NAME, mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
+ verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ }
+
+ @Test
+ public void canBeTethered() throws Exception {
+ initStateMachine(ConnectivityManager.TETHERING_BLUETOOTH);
+
+ dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED);
+ InOrder inOrder = inOrder(mTetherHelper, mNMService);
+ inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
+ inOrder.verify(mTetherHelper).notifyInterfaceStateChange(
+ IFACE_NAME, mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
+ verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ }
+
+ @Test
+ public void canUnrequestTethering() throws Exception {
+ initTetheredStateMachine(ConnectivityManager.TETHERING_BLUETOOTH, null);
+
+ dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
+ InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper);
+ inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
+ inOrder.verify(mTetherHelper).notifyInterfaceStateChange(
+ IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
+ verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ }
+
+ @Test
+ public void canBeTetheredAsUsb() throws Exception {
+ initStateMachine(ConnectivityManager.TETHERING_USB);
+
+ dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED);
+ InOrder inOrder = inOrder(mTetherHelper, mNMService);
+ inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME);
+ inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
+ inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
+ inOrder.verify(mTetherHelper).notifyInterfaceStateChange(
+ IFACE_NAME, mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
+ verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ }
+
+ @Test
+ public void handlesFirstUpstreamChange() throws Exception {
+ initTetheredStateMachine(ConnectivityManager.TETHERING_BLUETOOTH, null);
+
+ // Telling the state machine about its upstream interface triggers a little more configuration.
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE);
+ InOrder inOrder = inOrder(mNMService);
+ inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE);
+ inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
+ verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ }
+
+ @Test
+ public void handlesChangingUpstream() throws Exception {
+ initTetheredStateMachine(ConnectivityManager.TETHERING_BLUETOOTH, UPSTREAM_IFACE);
+
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
+ InOrder inOrder = inOrder(mNMService, mStatsService);
+ inOrder.verify(mStatsService).forceUpdate();
+ inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
+ inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
+ inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
+ inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
+ verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ }
+
+ @Test
+ public void canUnrequestTetheringWithUpstream() throws Exception {
+ initTetheredStateMachine(ConnectivityManager.TETHERING_BLUETOOTH, UPSTREAM_IFACE);
+
+ dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
+ InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper);
+ inOrder.verify(mStatsService).forceUpdate();
+ inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
+ inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
+ inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
+ inOrder.verify(mTetherHelper).notifyInterfaceStateChange(
+ IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
+ verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ }
+
+ @Test
+ public void interfaceDownLeadsToUnavailable() throws Exception {
+ for (boolean shouldThrow : new boolean[]{true, false}) {
+ initTetheredStateMachine(ConnectivityManager.TETHERING_USB, null);
+
+ if (shouldThrow) {
+ doThrow(RemoteException.class).when(mNMService).untetherInterface(IFACE_NAME);
+ }
+ dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
+ InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
+ usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
+ usbTeardownOrder.verify(mNMService).setInterfaceConfig(
+ IFACE_NAME, mInterfaceConfiguration);
+ usbTeardownOrder.verify(mTetherHelper).notifyInterfaceStateChange(
+ IFACE_NAME, mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
+ }
+ }
+
+ @Test
+ public void usbShouldBeTornDownOnTetherError() throws Exception {
+ initStateMachine(ConnectivityManager.TETHERING_USB);
+
+ doThrow(RemoteException.class).when(mNMService).tetherInterface(IFACE_NAME);
+ dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED);
+ InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
+ usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
+ usbTeardownOrder.verify(mNMService).setInterfaceConfig(
+ IFACE_NAME, mInterfaceConfiguration);
+ usbTeardownOrder.verify(mTetherHelper).notifyInterfaceStateChange(
+ IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
+ }
+
+ @Test
+ public void shouldTearDownUsbOnUpstreamError() throws Exception {
+ initTetheredStateMachine(ConnectivityManager.TETHERING_USB, null);
+
+ doThrow(RemoteException.class).when(mNMService).enableNat(anyString(), anyString());
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE);
+ InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
+ usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
+ usbTeardownOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
+ usbTeardownOrder.verify(mTetherHelper).notifyInterfaceStateChange(
+ IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
+ }
+
+ /**
+ * Send a command to the state machine under test, and run the event loop to idle.
+ *
+ * @param command One of the TetherInterfaceStateMachine.CMD_* constants.
+ */
+ private void dispatchCommand(int command) {
+ mTestedSm.sendMessage(command);
+ mLooper.dispatchAll();
+ }
+
+ /**
+ * Special override to tell the state machine that the upstream interface has changed.
+ *
+ * @see #dispatchCommand(int)
+ * @param upstreamIface String name of upstream interface (or null)
+ */
+ private void dispatchTetherConnectionChanged(String upstreamIface) {
+ mTestedSm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
+ upstreamIface);
+ mLooper.dispatchAll();
+ }
+}
\ No newline at end of file
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 3a2e946..2d96bff 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -27,7 +27,6 @@
import android.content.pm.PackageManager;
import android.net.wifi.WifiInfo;
import android.os.Build.VERSION_CODES;
-import android.os.Build;
import android.os.Bundle;
import android.os.Process;
import android.os.UserHandle;
@@ -42,7 +41,6 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
-import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@@ -62,7 +60,6 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.validateMockitoUsage;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -454,8 +451,7 @@
.thenReturn(true);
dpm.removeActiveAdmin(admin1);
-
- assertTrue(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+ assertFalse(dpm.isAdminActiveAsUser(admin1, DpmMockContext.CALLER_USER_HANDLE));
}
/**
@@ -479,8 +475,7 @@
mContext.binder.callingUid = 1234567;
dpms.removeActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE);
-
- assertTrue(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+ assertFalse(dpm.isAdminActiveAsUser(admin1, DpmMockContext.CALLER_USER_HANDLE));
// TODO DO Still can't be removed in this case.
}
@@ -510,28 +505,18 @@
mContext.callerPermissions.clear();
dpm.removeActiveAdmin(admin1);
- final ArgumentCaptor<BroadcastReceiver> brCap =
- ArgumentCaptor.forClass(BroadcastReceiver.class);
-
- // Is removing now, but not removed yet.
- assertTrue(dpm.isAdminActive(admin1));
- assertTrue(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
-
verify(mContext.spiedContext).sendOrderedBroadcastAsUser(
MockUtils.checkIntentAction(
DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED),
MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE),
isNull(String.class),
- brCap.capture(),
+ any(BroadcastReceiver.class),
eq(dpms.mHandler),
eq(Activity.RESULT_OK),
isNull(String.class),
isNull(Bundle.class));
- brCap.getValue().onReceive(mContext, null);
-
- assertFalse(dpm.isAdminActive(admin1));
- assertFalse(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+ assertFalse(dpm.isAdminActiveAsUser(admin1, DpmMockContext.CALLER_USER_HANDLE));
// Again broadcast from saveSettingsLocked().
verify(mContext.spiedContext, times(2)).sendBroadcastAsUser(
@@ -853,9 +838,7 @@
MockUtils.checkUserRestrictions()
);
- assertTrue(dpm.isAdminActive(admin1));
- assertTrue(dpm.isRemovingAdmin(admin1, UserHandle.USER_SYSTEM));
-
+ assertFalse(dpm.isAdminActiveAsUser(admin1, UserHandle.USER_SYSTEM));
// TODO Check other calls.
}
@@ -945,7 +928,7 @@
// Check
assertFalse(dpm.isProfileOwnerApp(admin1.getPackageName()));
- assertTrue(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+ assertFalse(dpm.isAdminActiveAsUser(admin1, DpmMockContext.CALLER_USER_HANDLE));
}
public void testSetProfileOwner_failures() throws Exception {
@@ -1477,7 +1460,7 @@
// Remove PO.
dpm.clearProfileOwner(admin1);
-
+ dpm.setActiveAdmin(admin1, false);
// Test 4, Caller is DO now.
assertTrue(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM));
@@ -1526,6 +1509,7 @@
// Remove PO and add DO.
dpm.clearProfileOwner(admin1);
+ dpm.setActiveAdmin(admin1, false);
assertTrue(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM));
// admin1 is DO.
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 f6e35f5..0783afc 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -527,6 +527,7 @@
int initialCode, String initialData, Bundle initialExtras) {
spiedContext.sendOrderedBroadcastAsUser(intent, user, receiverPermission, resultReceiver,
scheduler, initialCode, initialData, initialExtras);
+ resultReceiver.onReceive(spiedContext, intent);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 83a59fd..d51f2d8 100644
--- a/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -76,6 +76,7 @@
mService.setVibrator(mVibrator);
mService.setSystemReady(true);
mService.setHandler(mHandler);
+ mService.setSystemNotificationSound("beep!");
}
//
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
new file mode 100644
index 0000000..1c7a138
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -0,0 +1,1929 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.cloneShortcutList;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.hashSet;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.makeBundle;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.IUidObserver;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ILauncherApps;
+import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.content.pm.ShortcutServiceInternal;
+import android.content.pm.Signature;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PersistableBundle;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.test.InstrumentationTestCase;
+import android.test.mock.MockContext;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.pm.LauncherAppsService.LauncherAppsImpl;
+import com.android.server.pm.ShortcutUser.PackageWithUser;
+
+import org.junit.Assert;
+import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.BiPredicate;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
+ protected static final String TAG = "ShortcutManagerTest";
+
+ protected static final boolean DUMP_IN_TEARDOWN = false; // DO NOT SUBMIT WITH true
+
+ /**
+ * Whether to enable dump or not. Should be only true when debugging to avoid bugs where
+ * dump affecting the behavior.
+ */
+ protected static final boolean ENABLE_DUMP = false // DO NOT SUBMIT WITH true
+ || DUMP_IN_TEARDOWN || ShortcutService.DEBUG;
+
+ protected static final String[] EMPTY_STRINGS = new String[0]; // Just for readability.
+
+ protected static final String MAIN_ACTIVITY_CLASS = "MainActivity";
+
+ // public for mockito
+ public class BaseContext extends MockContext {
+ @Override
+ public Object getSystemService(String name) {
+ switch (name) {
+ case Context.USER_SERVICE:
+ return mMockUserManager;
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getSystemServiceName(Class<?> serviceClass) {
+ return getTestContext().getSystemServiceName(serviceClass);
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return mMockPackageManager;
+ }
+
+ @Override
+ public Resources getResources() {
+ return getTestContext().getResources();
+ }
+
+ @Override
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+ IntentFilter filter, String broadcastPermission, Handler scheduler) {
+ // ignore.
+ return null;
+ }
+
+ @Override
+ public void unregisterReceiver(BroadcastReceiver receiver) {
+ // ignore.
+ }
+ }
+
+ /** Context used in the client side */
+ public class ClientContext extends BaseContext {
+ @Override
+ public String getPackageName() {
+ return mInjectedClientPackage;
+ }
+
+ @Override
+ public int getUserId() {
+ return getCallingUserId();
+ }
+ }
+
+ /** Context used in the service side */
+ public class ServiceContext extends BaseContext {
+ long injectClearCallingIdentity() {
+ final int prevCallingUid = mInjectedCallingUid;
+ mInjectedCallingUid = Process.SYSTEM_UID;
+ return prevCallingUid;
+ }
+
+ void injectRestoreCallingIdentity(long token) {
+ mInjectedCallingUid = (int) token;
+ }
+
+ @Override
+ public int getUserId() {
+ return UserHandle.USER_SYSTEM;
+ }
+
+ public PackageInfo injectGetActivitiesWithMetadata(
+ String packageName, @UserIdInt int userId) {
+ return BaseShortcutManagerTest.this.injectGetActivitiesWithMetadata(packageName, userId);
+ }
+
+ public XmlResourceParser injectXmlMetaData(ActivityInfo activityInfo, String key) {
+ return BaseShortcutManagerTest.this.injectXmlMetaData(activityInfo, key);
+ }
+ }
+
+ /** ShortcutService with injection override methods. */
+ protected final class ShortcutServiceTestable extends ShortcutService {
+ final ServiceContext mContext;
+ IUidObserver mUidObserver;
+
+ public ShortcutServiceTestable(ServiceContext context, Looper looper) {
+ super(context, looper, /* onyForPackageManagerApis */ false);
+ mContext = context;
+ }
+
+ @Override
+ public String injectGetLocaleTagsForUser(@UserIdInt int userId) {
+ return mInjectedLocale.toLanguageTag();
+ }
+
+ @Override
+ boolean injectShouldPerformVerification() {
+ return true; // Always verify during unit tests.
+ }
+
+ @Override
+ String injectShortcutManagerConstants() {
+ return ConfigConstants.KEY_RESET_INTERVAL_SEC + "=" + (INTERVAL / 1000) + ","
+ + ConfigConstants.KEY_MAX_SHORTCUTS + "=" + MAX_SHORTCUTS + ","
+ + ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL + "="
+ + MAX_UPDATES_PER_INTERVAL + ","
+ + ConfigConstants.KEY_MAX_ICON_DIMENSION_DP + "=" + MAX_ICON_DIMENSION + ","
+ + ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM + "="
+ + MAX_ICON_DIMENSION_LOWRAM + ","
+ + ConfigConstants.KEY_ICON_FORMAT + "=PNG,"
+ + ConfigConstants.KEY_ICON_QUALITY + "=100";
+ }
+
+ @Override
+ long injectClearCallingIdentity() {
+ return mContext.injectClearCallingIdentity();
+ }
+
+ @Override
+ void injectRestoreCallingIdentity(long token) {
+ mContext.injectRestoreCallingIdentity(token);
+ }
+
+ @Override
+ int injectDipToPixel(int dip) {
+ return dip;
+ }
+
+ @Override
+ long injectCurrentTimeMillis() {
+ return mInjectedCurrentTimeMillis;
+ }
+
+ @Override
+ long injectElapsedRealtime() {
+ // TODO This should be kept separately from mInjectedCurrentTimeMillis, since
+ // this should increase even if we rewind mInjectedCurrentTimeMillis in some tests.
+ return mInjectedCurrentTimeMillis - START_TIME;
+ }
+
+ @Override
+ int injectBinderCallingUid() {
+ return mInjectedCallingUid;
+ }
+
+ @Override
+ int injectGetPackageUid(String packageName, int userId) {
+ return getInjectedPackageInfo(packageName, userId, false).applicationInfo.uid;
+ }
+
+ @Override
+ File injectSystemDataPath() {
+ return new File(mInjectedFilePathRoot, "system");
+ }
+
+ @Override
+ File injectUserDataPath(@UserIdInt int userId) {
+ return new File(mInjectedFilePathRoot, "user-" + userId);
+ }
+
+ @Override
+ void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) {
+ // Can't check
+ }
+
+ @Override
+ boolean injectIsLowRamDevice() {
+ return mInjectedIsLowRamDevice;
+ }
+
+ @Override
+ void injectRegisterUidObserver(IUidObserver observer, int which) {
+ mUidObserver = observer;
+ }
+
+ @Override
+ boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
+ return mDefaultLauncherChecker.test(callingPackage, userId);
+ }
+
+ @Override
+ PackageInfo injectPackageInfoWithUninstalled(String packageName, @UserIdInt int userId,
+ boolean getSignatures) {
+ return getInjectedPackageInfo(packageName, userId, getSignatures);
+ }
+
+ @Override
+ ApplicationInfo injectApplicationInfoWithUninstalled(
+ String packageName, @UserIdInt int userId) {
+ PackageInfo pi = injectPackageInfoWithUninstalled(
+ packageName, userId, /* getSignatures= */ false);
+ return pi != null ? pi.applicationInfo : null;
+ }
+
+ @Override
+ List<PackageInfo> injectGetPackagesWithUninstalled(@UserIdInt int userId) {
+ return BaseShortcutManagerTest.this.getInstalledPackagesWithUninstalled(userId);
+ }
+
+ @Override
+ ActivityInfo injectGetActivityInfoWithMetadataWithUninstalled(ComponentName activity,
+ @UserIdInt int userId) {
+ final PackageInfo pi = mContext.injectGetActivitiesWithMetadata(
+ activity.getPackageName(), userId);
+ if (pi == null || pi.activities == null) {
+ return null;
+ }
+ for (ActivityInfo ai : pi.activities) {
+ if (!mEnabledActivityChecker.test(ai.getComponentName(), userId)) {
+ continue;
+ }
+ if (activity.equals(ai.getComponentName())) {
+ return ai;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ boolean injectIsMainActivity(@NonNull ComponentName activity, int userId) {
+ if (!mEnabledActivityChecker.test(activity, userId)) {
+ return false;
+ }
+ return mMainActivityChecker.test(activity, userId);
+ }
+
+ @Override
+ List<ResolveInfo> injectGetMainActivities(@NonNull String packageName, int userId) {
+ final PackageInfo pi = mContext.injectGetActivitiesWithMetadata(
+ packageName, userId);
+ if (pi == null || pi.activities == null) {
+ return null;
+ }
+ final ArrayList<ResolveInfo> ret = new ArrayList<>(pi.activities.length);
+ for (int i = 0; i < pi.activities.length; i++) {
+ if (!mEnabledActivityChecker.test(pi.activities[i].getComponentName(), userId)) {
+ continue;
+ }
+ final ResolveInfo ri = new ResolveInfo();
+ ri.activityInfo = pi.activities[i];
+ ret.add(ri);
+ }
+
+ return ret;
+ }
+
+ @Override
+ ComponentName injectGetDefaultMainActivity(@NonNull String packageName, int userId) {
+ return mMainActivityFetcher.apply(packageName, userId);
+ }
+
+ @Override
+ boolean injectIsActivityEnabledAndExported(ComponentName activity, @UserIdInt int userId) {
+ return mEnabledActivityChecker.test(activity, userId);
+ }
+
+ @Override
+ XmlResourceParser injectXmlMetaData(ActivityInfo activityInfo, String key) {
+ return mContext.injectXmlMetaData(activityInfo, key);
+ }
+
+ @Override
+ void injectPostToHandler(Runnable r) {
+ runOnHandler(r);
+ }
+
+ @Override
+ void injectEnforceCallingPermission(String permission, String message) {
+ if (!mCallerPermissions.contains(permission)) {
+ throw new SecurityException("Missing permission: " + permission);
+ }
+ }
+
+ @Override
+ boolean injectIsSafeModeEnabled() {
+ return mSafeMode;
+ }
+
+ @Override
+ void wtf(String message, Throwable th) {
+ // During tests, WTF is fatal.
+ fail(message + " exception: " + th + "\n" + Log.getStackTraceString(th));
+ }
+ }
+
+ /** ShortcutManager with injection override methods. */
+ protected class ShortcutManagerTestable extends ShortcutManager {
+ public ShortcutManagerTestable(Context context, ShortcutServiceTestable service) {
+ super(context, service);
+ }
+
+ @Override
+ protected int injectMyUserId() {
+ return UserHandle.getUserId(mInjectedCallingUid);
+ }
+
+ @Override
+ public boolean setDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
+ // Note to simulate the binder RPC, we need to clone the incoming arguments.
+ // Otherwise bad things will happen because they're mutable.
+ return super.setDynamicShortcuts(cloneShortcutList(shortcutInfoList));
+ }
+
+ @Override
+ public boolean addDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
+ // Note to simulate the binder RPC, we need to clone the incoming arguments.
+ return super.addDynamicShortcuts(cloneShortcutList(shortcutInfoList));
+ }
+
+ @Override
+ public boolean updateShortcuts(List<ShortcutInfo> shortcutInfoList) {
+ // Note to simulate the binder RPC, we need to clone the incoming arguments.
+ return super.updateShortcuts(cloneShortcutList(shortcutInfoList));
+ }
+ }
+
+ protected class LauncherAppImplTestable extends LauncherAppsImpl {
+ final ServiceContext mContext;
+
+ public LauncherAppImplTestable(ServiceContext context) {
+ super(context);
+ mContext = context;
+ }
+
+ @Override
+ public void verifyCallingPackage(String callingPackage) {
+ // SKIP
+ }
+
+ @Override
+ void postToPackageMonitorHandler(Runnable r) {
+ runOnHandler(r);
+ }
+
+ @Override
+ int injectBinderCallingUid() {
+ return mInjectedCallingUid;
+ }
+
+ @Override
+ long injectClearCallingIdentity() {
+ final int prevCallingUid = mInjectedCallingUid;
+ mInjectedCallingUid = Process.SYSTEM_UID;
+ return prevCallingUid;
+ }
+
+ @Override
+ void injectRestoreCallingIdentity(long token) {
+ mInjectedCallingUid = (int) token;
+ }
+ }
+
+ protected class LauncherAppsTestable extends LauncherApps {
+ public LauncherAppsTestable(Context context, ILauncherApps service) {
+ super(context, service);
+ }
+ }
+
+ public static class ShortcutActivity extends Activity {
+ }
+
+ public static class ShortcutActivity2 extends Activity {
+ }
+
+ public static class ShortcutActivity3 extends Activity {
+ }
+
+ protected Looper mLooper;
+ protected Handler mHandler;
+
+ protected ServiceContext mServiceContext;
+ protected ClientContext mClientContext;
+
+ protected ShortcutServiceTestable mService;
+ protected ShortcutManagerTestable mManager;
+ protected ShortcutServiceInternal mInternal;
+
+ protected LauncherAppImplTestable mLauncherAppImpl;
+
+ // LauncherApps has per-instace state, so we need a differnt instance for each launcher.
+ protected final Map<Pair<Integer, String>, LauncherAppsTestable>
+ mLauncherAppsMap = new HashMap<>();
+ protected LauncherAppsTestable mLauncherApps; // Current one
+
+ protected File mInjectedFilePathRoot;
+
+ protected boolean mSafeMode;
+
+ protected long mInjectedCurrentTimeMillis;
+
+ protected boolean mInjectedIsLowRamDevice;
+
+ protected Locale mInjectedLocale = Locale.ENGLISH;
+
+ protected int mInjectedCallingUid;
+ protected String mInjectedClientPackage;
+
+ protected Map<String, PackageInfo> mInjectedPackages;
+
+ protected Set<PackageWithUser> mUninstalledPackages;
+
+ protected PackageManager mMockPackageManager;
+ protected PackageManagerInternal mMockPackageManagerInternal;
+ protected UserManager mMockUserManager;
+ protected UsageStatsManagerInternal mMockUsageStatsManagerInternal;
+ protected ActivityManagerInternal mMockActivityManagerInternal;
+
+ protected static final String CALLING_PACKAGE_1 = "com.android.test.1";
+ protected static final int CALLING_UID_1 = 10001;
+
+ protected static final String CALLING_PACKAGE_2 = "com.android.test.2";
+ protected static final int CALLING_UID_2 = 10002;
+
+ protected static final String CALLING_PACKAGE_3 = "com.android.test.3";
+ protected static final int CALLING_UID_3 = 10003;
+
+ protected static final String CALLING_PACKAGE_4 = "com.android.test.4";
+ protected static final int CALLING_UID_4 = 10004;
+
+ protected static final String LAUNCHER_1 = "com.android.launcher.1";
+ protected static final int LAUNCHER_UID_1 = 10011;
+
+ protected static final String LAUNCHER_2 = "com.android.launcher.2";
+ protected static final int LAUNCHER_UID_2 = 10012;
+
+ protected static final String LAUNCHER_3 = "com.android.launcher.3";
+ protected static final int LAUNCHER_UID_3 = 10013;
+
+ protected static final String LAUNCHER_4 = "com.android.launcher.4";
+ protected static final int LAUNCHER_UID_4 = 10014;
+
+ protected static final int USER_0 = UserHandle.USER_SYSTEM;
+ protected static final int USER_10 = 10;
+ protected static final int USER_11 = 11;
+ protected static final int USER_P0 = 20; // profile of user 0
+
+ protected static final UserHandle HANDLE_USER_0 = UserHandle.of(USER_0);
+ protected static final UserHandle HANDLE_USER_10 = UserHandle.of(USER_10);
+ protected static final UserHandle HANDLE_USER_11 = UserHandle.of(USER_11);
+ protected static final UserHandle HANDLE_USER_P0 = UserHandle.of(USER_P0);
+
+ protected static final UserInfo USER_INFO_0 = withProfileGroupId(
+ new UserInfo(USER_0, "user0",
+ UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_INITIALIZED), 10);
+
+ protected static final UserInfo USER_INFO_10 =
+ new UserInfo(USER_10, "user10", UserInfo.FLAG_INITIALIZED);
+
+ protected static final UserInfo USER_INFO_11 =
+ new UserInfo(USER_11, "user11", UserInfo.FLAG_INITIALIZED);
+
+ protected static final UserInfo USER_INFO_P0 = withProfileGroupId(
+ new UserInfo(USER_P0, "userP0",
+ UserInfo.FLAG_MANAGED_PROFILE), 10);
+
+ protected BiPredicate<String, Integer> mDefaultLauncherChecker =
+ (callingPackage, userId) ->
+ LAUNCHER_1.equals(callingPackage) || LAUNCHER_2.equals(callingPackage)
+ || LAUNCHER_3.equals(callingPackage) || LAUNCHER_4.equals(callingPackage);
+
+ protected BiPredicate<ComponentName, Integer> mMainActivityChecker =
+ (activity, userId) -> true;
+
+ protected BiFunction<String, Integer, ComponentName> mMainActivityFetcher =
+ (packageName, userId) -> new ComponentName(packageName, MAIN_ACTIVITY_CLASS);
+
+ protected BiPredicate<ComponentName, Integer> mEnabledActivityChecker
+ = (activity, userId) -> true; // all activities are enabled.
+
+ protected static final long START_TIME = 1440000000101L;
+
+ protected static final long INTERVAL = 10000;
+
+ protected static final int MAX_SHORTCUTS = 10;
+
+ protected static final int MAX_UPDATES_PER_INTERVAL = 3;
+
+ protected static final int MAX_ICON_DIMENSION = 128;
+
+ protected static final int MAX_ICON_DIMENSION_LOWRAM = 32;
+
+ protected static final ShortcutQuery QUERY_ALL = new ShortcutQuery();
+
+ protected final ArrayList<String> mCallerPermissions = new ArrayList<>();
+
+ protected final HashMap<String, LinkedHashMap<ComponentName, Integer>> mActivityMetadataResId
+ = new HashMap<>();
+
+ protected final Map<Integer, UserInfo> mUserInfos = new HashMap<>();
+ protected final Map<Integer, Boolean> mRunningUsers = new HashMap<>();
+ protected final Map<Integer, Boolean> mUnlockedUsers = new HashMap<>();
+
+ protected static final String PACKAGE_SYSTEM_LAUNCHER = "com.android.systemlauncher";
+ protected static final String PACKAGE_SYSTEM_LAUNCHER_NAME = "systemlauncher_name";
+ protected static final int PACKAGE_SYSTEM_LAUNCHER_PRIORITY = 0;
+
+ protected static final String PACKAGE_FALLBACK_LAUNCHER = "com.android.settings";
+ protected static final String PACKAGE_FALLBACK_LAUNCHER_NAME = "fallback";
+ protected static final int PACKAGE_FALLBACK_LAUNCHER_PRIORITY = -999;
+
+ static {
+ QUERY_ALL.setQueryFlags(
+ ShortcutQuery.FLAG_GET_ALL_KINDS);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mLooper = Looper.getMainLooper();
+ mHandler = new Handler(mLooper);
+
+ mServiceContext = spy(new ServiceContext());
+ mClientContext = new ClientContext();
+
+ mMockPackageManager = mock(PackageManager.class);
+ mMockPackageManagerInternal = mock(PackageManagerInternal.class);
+ mMockUserManager = mock(UserManager.class);
+ mMockUsageStatsManagerInternal = mock(UsageStatsManagerInternal.class);
+ mMockActivityManagerInternal = mock(ActivityManagerInternal.class);
+
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
+ LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
+ LocalServices.addService(UsageStatsManagerInternal.class, mMockUsageStatsManagerInternal);
+ LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ LocalServices.addService(ActivityManagerInternal.class, mMockActivityManagerInternal);
+
+ // Prepare injection values.
+
+ mInjectedCurrentTimeMillis = START_TIME;
+
+ mInjectedPackages = new HashMap<>();
+ addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 1);
+ addPackage(CALLING_PACKAGE_2, CALLING_UID_2, 2);
+ addPackage(CALLING_PACKAGE_3, CALLING_UID_3, 3);
+ addPackage(CALLING_PACKAGE_4, CALLING_UID_4, 10);
+ addPackage(LAUNCHER_1, LAUNCHER_UID_1, 4);
+ addPackage(LAUNCHER_2, LAUNCHER_UID_2, 5);
+ addPackage(LAUNCHER_3, LAUNCHER_UID_3, 6);
+ addPackage(LAUNCHER_4, LAUNCHER_UID_4, 10);
+
+ // CALLING_PACKAGE_3 / LAUNCHER_3 are not backup target.
+ updatePackageInfo(CALLING_PACKAGE_3,
+ pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
+ updatePackageInfo(LAUNCHER_3,
+ pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
+
+ mUninstalledPackages = new HashSet<>();
+
+ mInjectedFilePathRoot = new File(getTestContext().getCacheDir(), "test-files");
+
+ deleteAllSavedFiles();
+
+ // Set up users.
+ when(mMockUserManager.getUserInfo(anyInt())).thenAnswer(new AnswerWithSystemCheck<>(
+ inv -> mUserInfos.get((Integer) inv.getArguments()[0])));
+
+ mUserInfos.put(USER_0, USER_INFO_0);
+ mUserInfos.put(USER_10, USER_INFO_10);
+ mUserInfos.put(USER_11, USER_INFO_11);
+ mUserInfos.put(USER_P0, USER_INFO_P0);
+
+ // Set up isUserRunning and isUserUnlocked.
+ when(mMockUserManager.isUserRunning(anyInt())).thenAnswer(new AnswerWithSystemCheck<>(
+ inv -> b(mRunningUsers.get((Integer) inv.getArguments()[0]))));
+
+ when(mMockUserManager.isUserUnlocked(anyInt()))
+ .thenAnswer(new AnswerWithSystemCheck<>(inv -> {
+ final int userId = (Integer) inv.getArguments()[0];
+ return b(mRunningUsers.get(userId)) && b(mUnlockedUsers.get(userId));
+ }));
+ // isUserUnlockingOrUnlocked() return the same value as isUserUnlocked().
+ when(mMockUserManager.isUserUnlockingOrUnlocked(anyInt()))
+ .thenAnswer(new AnswerWithSystemCheck<>(inv -> {
+ final int userId = (Integer) inv.getArguments()[0];
+ return b(mRunningUsers.get(userId)) && b(mUnlockedUsers.get(userId));
+ }));
+
+ when(mMockActivityManagerInternal.getUidProcessState(anyInt())).thenReturn(
+ ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+
+ // User 0 and P0 are always running
+ mRunningUsers.put(USER_0, true);
+ mRunningUsers.put(USER_10, false);
+ mRunningUsers.put(USER_11, false);
+ mRunningUsers.put(USER_P0, true);
+
+ // Unlock all users by default.
+ mUnlockedUsers.put(USER_0, true);
+ mUnlockedUsers.put(USER_10, true);
+ mUnlockedUsers.put(USER_11, true);
+ mUnlockedUsers.put(USER_P0, true);
+
+ // Set up resources
+ setUpAppResources();
+
+ // Start the service.
+ initService();
+ setCaller(CALLING_PACKAGE_1);
+ }
+
+ private static boolean b(Boolean value) {
+ return (value != null && value);
+ }
+
+ /**
+ * Returns a boolean but also checks if the current UID is SYSTEM_UID.
+ */
+ protected class AnswerWithSystemCheck<T> implements Answer<T> {
+ private final Function<InvocationOnMock, T> mChecker;
+
+ public AnswerWithSystemCheck(Function<InvocationOnMock, T> checker) {
+ mChecker = checker;
+ }
+
+ @Override
+ public T answer(InvocationOnMock invocation) throws Throwable {
+ assertEquals("Must be called on SYSTEM UID.",
+ Process.SYSTEM_UID, mInjectedCallingUid);
+ return mChecker.apply(invocation);
+ }
+ }
+
+ protected void setUpAppResources() throws Exception {
+ setUpAppResources(/* offset = */ 0);
+ }
+
+ protected void setUpAppResources(int ressIdOffset) throws Exception {
+ // ressIdOffset is used to adjust resource IDs to emulate the case where an updated app
+ // has resource IDs changed.
+
+ doAnswer(pmInvocation -> {
+ assertEquals(Process.SYSTEM_UID, mInjectedCallingUid);
+
+ final String packageName = (String) pmInvocation.getArguments()[0];
+ final int userId = (Integer) pmInvocation.getArguments()[1];
+
+ final Resources res = mock(Resources.class);
+
+ doAnswer(resInvocation -> {
+ final int argResId = (Integer) resInvocation.getArguments()[0];
+
+ return "string-" + packageName + "-user:" + userId + "-res:" + argResId
+ + "/" + mInjectedLocale;
+ }).when(res).getString(anyInt());
+
+ doAnswer(resInvocation -> {
+ final int resId = (Integer) resInvocation.getArguments()[0];
+
+ // Always use the "string" resource type. The type doesn't matter during the test.
+ return packageName + ":string/r" + resId;
+ }).when(res).getResourceName(anyInt());
+
+ doAnswer(resInvocation -> {
+ final String argResName = (String) resInvocation.getArguments()[0];
+ final String argType = (String) resInvocation.getArguments()[1];
+ final String argPackageName = (String) resInvocation.getArguments()[2];
+
+ // See the above code. getResourceName() will just use "r" + res ID as the entry
+ // name.
+ String entryName = argResName;
+ if (entryName.contains("/")) {
+ entryName = ShortcutInfo.getResourceEntryName(entryName);
+ }
+ return Integer.parseInt(entryName.substring(1)) + ressIdOffset;
+ }).when(res).getIdentifier(anyString(), anyString(), anyString());
+ return res;
+ }).when(mMockPackageManager).getResourcesForApplicationAsUser(anyString(), anyInt());
+ }
+
+ protected static UserInfo withProfileGroupId(UserInfo in, int groupId) {
+ in.profileGroupId = groupId;
+ return in;
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (DUMP_IN_TEARDOWN) dumpsysOnLogcat("Teardown");
+
+ shutdownServices();
+
+ super.tearDown();
+ }
+
+ protected Context getTestContext() {
+ return getInstrumentation().getContext();
+ }
+
+ protected ShortcutManager getManager() {
+ return mManager;
+ }
+
+ protected void deleteAllSavedFiles() {
+ // Empty the data directory.
+ if (mInjectedFilePathRoot.exists()) {
+ Assert.assertTrue("failed to delete dir",
+ FileUtils.deleteContents(mInjectedFilePathRoot));
+ }
+ mInjectedFilePathRoot.mkdirs();
+ }
+
+ /** (Re-) init the manager and the service. */
+ protected void initService() {
+ shutdownServices();
+
+ LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
+
+ // Instantiate targets.
+ mService = new ShortcutServiceTestable(mServiceContext, mLooper);
+ mManager = new ShortcutManagerTestable(mClientContext, mService);
+
+ mInternal = LocalServices.getService(ShortcutServiceInternal.class);
+
+ mLauncherAppImpl = new LauncherAppImplTestable(mServiceContext);
+ mLauncherApps = null;
+ mLauncherAppsMap.clear();
+
+ // Send boot sequence events.
+ mService.onBootPhase(SystemService.PHASE_LOCK_SETTINGS_READY);
+
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ }
+
+ protected void shutdownServices() {
+ if (mService != null) {
+ // Flush all the unsaved data from the previous instance.
+ mService.saveDirtyInfo();
+
+ // Make sure everything is consistent.
+ mService.verifyStates();
+ }
+ LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
+
+ mService = null;
+ mManager = null;
+ mInternal = null;
+ mLauncherAppImpl = null;
+ mLauncherApps = null;
+ mLauncherAppsMap.clear();
+ }
+
+ protected void runOnHandler(Runnable r) {
+ final long token = mServiceContext.injectClearCallingIdentity();
+ try {
+ r.run();
+ } finally {
+ mServiceContext.injectRestoreCallingIdentity(token);
+ }
+ }
+
+ protected void addPackage(String packageName, int uid, int version) {
+ addPackage(packageName, uid, version, packageName);
+ }
+
+ protected Signature[] genSignatures(String... signatures) {
+ final Signature[] sigs = new Signature[signatures.length];
+ for (int i = 0; i < signatures.length; i++){
+ sigs[i] = new Signature(signatures[i].getBytes());
+ }
+ return sigs;
+ }
+
+ protected PackageInfo genPackage(String packageName, int uid, int version, String... signatures) {
+ final PackageInfo pi = new PackageInfo();
+ pi.packageName = packageName;
+ pi.applicationInfo = new ApplicationInfo();
+ pi.applicationInfo.uid = uid;
+ pi.applicationInfo.flags = ApplicationInfo.FLAG_INSTALLED
+ | ApplicationInfo.FLAG_ALLOW_BACKUP;
+ pi.versionCode = version;
+ pi.applicationInfo.versionCode = version;
+ pi.signatures = genSignatures(signatures);
+
+ return pi;
+ }
+
+ protected void addPackage(String packageName, int uid, int version, String... signatures) {
+ mInjectedPackages.put(packageName, genPackage(packageName, uid, version, signatures));
+ }
+
+ protected void updatePackageInfo(String packageName, Consumer<PackageInfo> c) {
+ c.accept(mInjectedPackages.get(packageName));
+ }
+
+ protected void updatePackageVersion(String packageName, int increment) {
+ updatePackageInfo(packageName, pi -> {
+ pi.versionCode += increment;
+ pi.applicationInfo.versionCode += increment;
+ });
+ }
+
+ protected void updatePackageLastUpdateTime(String packageName, long increment) {
+ updatePackageInfo(packageName, pi -> {
+ pi.lastUpdateTime += increment;
+ });
+ }
+
+ protected void uninstallPackage(int userId, String packageName) {
+ if (ENABLE_DUMP) {
+ Log.v(TAG, "Unnstall package " + packageName + " / " + userId);
+ }
+ mUninstalledPackages.add(PackageWithUser.of(userId, packageName));
+ }
+
+ protected void installPackage(int userId, String packageName) {
+ if (ENABLE_DUMP) {
+ Log.v(TAG, "Install package " + packageName + " / " + userId);
+ }
+ mUninstalledPackages.remove(PackageWithUser.of(userId, packageName));
+ }
+
+ PackageInfo getInjectedPackageInfo(String packageName, @UserIdInt int userId,
+ boolean getSignatures) {
+ final PackageInfo pi = mInjectedPackages.get(packageName);
+ if (pi == null) return null;
+
+ final PackageInfo ret = new PackageInfo();
+ ret.packageName = pi.packageName;
+ ret.versionCode = pi.versionCode;
+ ret.lastUpdateTime = pi.lastUpdateTime;
+
+ ret.applicationInfo = new ApplicationInfo(pi.applicationInfo);
+ ret.applicationInfo.uid = UserHandle.getUid(userId, pi.applicationInfo.uid);
+ ret.applicationInfo.packageName = pi.packageName;
+
+ if (mUninstalledPackages.contains(PackageWithUser.of(userId, packageName))) {
+ ret.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED;
+ }
+
+ if (getSignatures) {
+ ret.signatures = pi.signatures;
+ }
+
+ return ret;
+ }
+
+ protected void addApplicationInfo(PackageInfo pi, List<ApplicationInfo> list) {
+ if (pi != null && pi.applicationInfo != null) {
+ list.add(pi.applicationInfo);
+ }
+ }
+
+ protected List<ApplicationInfo> getInstalledApplications(int userId) {
+ final ArrayList<ApplicationInfo> ret = new ArrayList<>();
+
+ addApplicationInfo(getInjectedPackageInfo(CALLING_PACKAGE_1, userId, false), ret);
+ addApplicationInfo(getInjectedPackageInfo(CALLING_PACKAGE_2, userId, false), ret);
+ addApplicationInfo(getInjectedPackageInfo(CALLING_PACKAGE_3, userId, false), ret);
+ addApplicationInfo(getInjectedPackageInfo(CALLING_PACKAGE_4, userId, false), ret);
+ addApplicationInfo(getInjectedPackageInfo(LAUNCHER_1, userId, false), ret);
+ addApplicationInfo(getInjectedPackageInfo(LAUNCHER_2, userId, false), ret);
+ addApplicationInfo(getInjectedPackageInfo(LAUNCHER_3, userId, false), ret);
+ addApplicationInfo(getInjectedPackageInfo(LAUNCHER_4, userId, false), ret);
+
+ return ret;
+ }
+
+ private void addPackageInfo(PackageInfo pi, List<PackageInfo> list) {
+ if (pi != null) {
+ list.add(pi);
+ }
+ }
+
+ private List<PackageInfo> getInstalledPackagesWithUninstalled(int userId) {
+ final ArrayList<PackageInfo> ret = new ArrayList<>();
+
+ addPackageInfo(getInjectedPackageInfo(CALLING_PACKAGE_1, userId, false), ret);
+ addPackageInfo(getInjectedPackageInfo(CALLING_PACKAGE_2, userId, false), ret);
+ addPackageInfo(getInjectedPackageInfo(CALLING_PACKAGE_3, userId, false), ret);
+ addPackageInfo(getInjectedPackageInfo(CALLING_PACKAGE_4, userId, false), ret);
+ addPackageInfo(getInjectedPackageInfo(LAUNCHER_1, userId, false), ret);
+ addPackageInfo(getInjectedPackageInfo(LAUNCHER_2, userId, false), ret);
+ addPackageInfo(getInjectedPackageInfo(LAUNCHER_3, userId, false), ret);
+ addPackageInfo(getInjectedPackageInfo(LAUNCHER_4, userId, false), ret);
+
+ return ret;
+ }
+
+ protected void addManifestShortcutResource(ComponentName activity, int resId) {
+ final String packageName = activity.getPackageName();
+ LinkedHashMap<ComponentName, Integer> map = mActivityMetadataResId.get(packageName);
+ if (map == null) {
+ map = new LinkedHashMap<>();
+ mActivityMetadataResId.put(packageName, map);
+ }
+ map.put(activity, resId);
+ }
+
+ protected PackageInfo injectGetActivitiesWithMetadata(String packageName, @UserIdInt int userId) {
+ final PackageInfo ret = getInjectedPackageInfo(packageName, userId,
+ /* getSignatures=*/ false);
+
+ final HashMap<ComponentName, Integer> activities = mActivityMetadataResId.get(packageName);
+ if (activities != null) {
+ final ArrayList<ActivityInfo> list = new ArrayList<>();
+
+ for (ComponentName cn : activities.keySet()) {
+ ActivityInfo ai = new ActivityInfo();
+ ai.packageName = cn.getPackageName();
+ ai.name = cn.getClassName();
+ ai.metaData = new Bundle();
+ ai.metaData.putInt(ShortcutParser.METADATA_KEY, activities.get(cn));
+ ai.applicationInfo = ret.applicationInfo;
+ list.add(ai);
+ }
+ ret.activities = list.toArray(new ActivityInfo[list.size()]);
+ }
+ return ret;
+ }
+
+ protected XmlResourceParser injectXmlMetaData(ActivityInfo activityInfo, String key) {
+ if (!ShortcutParser.METADATA_KEY.equals(key) || activityInfo.metaData == null) {
+ return null;
+ }
+ final int resId = activityInfo.metaData.getInt(key);
+ return getTestContext().getResources().getXml(resId);
+ }
+
+ /** Replace the current calling package */
+ protected void setCaller(String packageName, int userId) {
+ mInjectedClientPackage = packageName;
+ mInjectedCallingUid =
+ Preconditions.checkNotNull(getInjectedPackageInfo(packageName, userId, false),
+ "Unknown package").applicationInfo.uid;
+
+ // Set up LauncherApps for this caller.
+ final Pair<Integer, String> key = Pair.create(userId, packageName);
+ if (!mLauncherAppsMap.containsKey(key)) {
+ mLauncherAppsMap.put(key, new LauncherAppsTestable(mClientContext, mLauncherAppImpl));
+ }
+ mLauncherApps = mLauncherAppsMap.get(key);
+ }
+
+ protected void setCaller(String packageName) {
+ setCaller(packageName, UserHandle.USER_SYSTEM);
+ }
+
+ protected String getCallingPackage() {
+ return mInjectedClientPackage;
+ }
+
+ protected void setDefaultLauncherChecker(BiPredicate<String, Integer> p) {
+ mDefaultLauncherChecker = p;
+ }
+
+ protected void runWithCaller(String packageName, int userId, Runnable r) {
+ final String previousPackage = mInjectedClientPackage;
+ final int previousUserId = UserHandle.getUserId(mInjectedCallingUid);
+
+ setCaller(packageName, userId);
+
+ r.run();
+
+ setCaller(previousPackage, previousUserId);
+ }
+
+ protected void runWithSystemUid(Runnable r) {
+ final int origUid = mInjectedCallingUid;
+ mInjectedCallingUid = Process.SYSTEM_UID;
+ r.run();
+ mInjectedCallingUid = origUid;
+ }
+
+ protected void lookupAndFillInResourceNames(ShortcutInfo si) {
+ runWithSystemUid(() -> si.lookupAndFillInResourceNames(
+ mService.injectGetResourcesForApplicationAsUser(si.getPackage(), si.getUserId())));
+ }
+
+ protected int getCallingUserId() {
+ return UserHandle.getUserId(mInjectedCallingUid);
+ }
+
+ protected UserHandle getCallingUser() {
+ return UserHandle.of(getCallingUserId());
+ }
+
+ /** For debugging */
+ protected void dumpsysOnLogcat() {
+ dumpsysOnLogcat("");
+ }
+
+ protected void dumpsysOnLogcat(String message) {
+ dumpsysOnLogcat(message, false);
+ }
+
+ protected void dumpsysOnLogcat(String message, boolean force) {
+ if (force || !ENABLE_DUMP) return;
+
+ Log.v(TAG, "Dumping ShortcutService: " + message);
+ for (String line : dumpsys(null).split("\n")) {
+ Log.v(TAG, line);
+ }
+ }
+
+ protected String dumpCheckin() {
+ return dumpsys(new String[]{"--checkin"});
+ }
+
+ private String dumpsys(String[] args) {
+ final ArrayList<String> origPermissions = new ArrayList<>(mCallerPermissions);
+ mCallerPermissions.add(android.Manifest.permission.DUMP);
+ try {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ final PrintWriter pw = new PrintWriter(out);
+ mService.dump(/* fd */ null, pw, args);
+ pw.close();
+
+ return out.toString();
+ } finally {
+ mCallerPermissions.clear();
+ mCallerPermissions.addAll(origPermissions);
+ }
+ }
+
+ /**
+ * For debugging, dump arbitrary file on logcat.
+ */
+ protected void dumpFileOnLogcat(String path) {
+ dumpFileOnLogcat(path, "");
+ }
+
+ protected void dumpFileOnLogcat(String path, String message) {
+ if (!ENABLE_DUMP) return;
+
+ Log.v(TAG, "Dumping file: " + path + " " + message);
+ final StringBuilder sb = new StringBuilder();
+ try (BufferedReader br = new BufferedReader(new FileReader(path))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ Log.v(TAG, line);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Couldn't read file", e);
+ fail("Exception " + e);
+ }
+ }
+
+ /**
+ * For debugging, dump the main state file on logcat.
+ */
+ protected void dumpBaseStateFile() {
+ mService.saveDirtyInfo();
+ dumpFileOnLogcat(mInjectedFilePathRoot.getAbsolutePath()
+ + "/system/" + ShortcutService.FILENAME_BASE_STATE);
+ }
+
+ /**
+ * For debugging, dump per-user state file on logcat.
+ */
+ protected void dumpUserFile(int userId) {
+ dumpUserFile(userId, "");
+ }
+
+ protected void dumpUserFile(int userId, String message) {
+ mService.saveDirtyInfo();
+ dumpFileOnLogcat(mInjectedFilePathRoot.getAbsolutePath()
+ + "/user-" + userId
+ + "/" + ShortcutService.FILENAME_USER_PACKAGES, message);
+ }
+
+ /**
+ * Make a shortcut with an ID.
+ */
+ protected ShortcutInfo makeShortcut(String id) {
+ return makeShortcut(
+ id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
+ makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+ }
+
+ protected ShortcutInfo makeShortcutWithTitle(String id, String title) {
+ return makeShortcut(
+ id, title, /* activity =*/ null, /* icon =*/ null,
+ makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+ }
+
+ /**
+ * Make a shortcut with an ID and timestamp.
+ */
+ protected ShortcutInfo makeShortcutWithTimestamp(String id, long timestamp) {
+ final ShortcutInfo s = makeShortcut(
+ id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
+ makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+ s.setTimestamp(timestamp);
+ return s;
+ }
+
+ /**
+ * Make a shortcut with an ID, a timestamp and an activity component
+ */
+ protected ShortcutInfo makeShortcutWithTimestampWithActivity(String id, long timestamp,
+ ComponentName activity) {
+ final ShortcutInfo s = makeShortcut(
+ id, "Title-" + id, activity, /* icon =*/ null,
+ makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+ s.setTimestamp(timestamp);
+ return s;
+ }
+
+ /**
+ * Make a shortcut with an ID and icon.
+ */
+ protected ShortcutInfo makeShortcutWithIcon(String id, Icon icon) {
+ return makeShortcut(
+ id, "Title-" + id, /* activity =*/ null, icon,
+ makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+ }
+
+ protected ShortcutInfo makePackageShortcut(String packageName, String id) {
+ String origCaller = getCallingPackage();
+
+ setCaller(packageName);
+ ShortcutInfo s = makeShortcut(
+ id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
+ makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+ setCaller(origCaller); // restore the caller
+
+ return s;
+ }
+
+ /**
+ * Make multiple shortcuts with IDs.
+ */
+ protected List<ShortcutInfo> makeShortcuts(String... ids) {
+ final ArrayList<ShortcutInfo> ret = new ArrayList();
+ for (String id : ids) {
+ ret.add(makeShortcut(id));
+ }
+ return ret;
+ }
+
+ protected ShortcutInfo.Builder makeShortcutBuilder() {
+ return new ShortcutInfo.Builder(mClientContext);
+ }
+
+ protected ShortcutInfo makeShortcutWithActivity(String id, ComponentName activity) {
+ return makeShortcut(
+ id, "Title-" + id, activity, /* icon =*/ null,
+ makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+ }
+
+ protected ShortcutInfo makeShortcutWithIntent(String id, Intent intent) {
+ return makeShortcut(
+ id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
+ intent, /* rank =*/ 0);
+ }
+
+ protected ShortcutInfo makeShortcutWithActivityAndTitle(String id, ComponentName activity,
+ String title) {
+ return makeShortcut(
+ id, title, activity, /* icon =*/ null,
+ makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+ }
+
+ protected ShortcutInfo makeShortcutWithActivityAndRank(String id, ComponentName activity,
+ int rank) {
+ return makeShortcut(
+ id, "Title-" + id, activity, /* icon =*/ null,
+ makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), rank);
+ }
+
+ /**
+ * Make a shortcut with details.
+ */
+ protected ShortcutInfo makeShortcut(String id, String title, ComponentName activity,
+ Icon icon, Intent intent, int rank) {
+ final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, id)
+ .setActivity(new ComponentName(mClientContext.getPackageName(), "dummy"))
+ .setShortLabel(title)
+ .setRank(rank)
+ .setIntent(intent);
+ if (icon != null) {
+ b.setIcon(icon);
+ }
+ if (activity != null) {
+ b.setActivity(activity);
+ }
+ final ShortcutInfo s = b.build();
+
+ s.setTimestamp(mInjectedCurrentTimeMillis); // HACK
+
+ return s;
+ }
+
+ protected ShortcutInfo makeShortcutWithIntents(String id, Intent... intents) {
+ return makeShortcut(
+ id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
+ intents, /* rank =*/ 0);
+ }
+
+ /**
+ * Make a shortcut with details.
+ */
+ protected ShortcutInfo makeShortcut(String id, String title, ComponentName activity,
+ Icon icon, Intent[] intents, int rank) {
+ final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, id)
+ .setActivity(new ComponentName(mClientContext.getPackageName(), "dummy"))
+ .setShortLabel(title)
+ .setRank(rank)
+ .setIntents(intents);
+ if (icon != null) {
+ b.setIcon(icon);
+ }
+ if (activity != null) {
+ b.setActivity(activity);
+ }
+ final ShortcutInfo s = b.build();
+
+ s.setTimestamp(mInjectedCurrentTimeMillis); // HACK
+
+ return s;
+ }
+
+ /**
+ * Make a shortcut with details.
+ */
+ protected ShortcutInfo makeShortcutWithExtras(String id, Intent intent,
+ PersistableBundle extras) {
+ final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, id)
+ .setActivity(new ComponentName(mClientContext.getPackageName(), "dummy"))
+ .setShortLabel("title-" + id)
+ .setExtras(extras)
+ .setIntent(intent);
+ final ShortcutInfo s = b.build();
+
+ s.setTimestamp(mInjectedCurrentTimeMillis); // HACK
+
+ return s;
+ }
+
+ /**
+ * Make an intent.
+ */
+ protected Intent makeIntent(String action, Class<?> clazz, Object... bundleKeysAndValues) {
+ final Intent intent = new Intent(action);
+ intent.setComponent(makeComponent(clazz));
+ intent.replaceExtras(makeBundle(bundleKeysAndValues));
+ return intent;
+ }
+
+ /**
+ * Make an component name, with the client context.
+ */
+ @NonNull
+ protected ComponentName makeComponent(Class<?> clazz) {
+ return new ComponentName(mClientContext, clazz);
+ }
+
+ @NonNull
+ protected ShortcutInfo findById(List<ShortcutInfo> list, String id) {
+ for (ShortcutInfo s : list) {
+ if (s.getId().equals(id)) {
+ return s;
+ }
+ }
+ fail("Shortcut with id " + id + " not found");
+ return null;
+ }
+
+ protected void assertSystem() {
+ assertEquals("Caller must be system", Process.SYSTEM_UID, mInjectedCallingUid);
+ }
+
+ protected void assertResetTimes(long expectedLastResetTime, long expectedNextResetTime) {
+ assertEquals(expectedLastResetTime, mService.getLastResetTimeLocked());
+ assertEquals(expectedNextResetTime, mService.getNextResetTimeLocked());
+ }
+
+ public static List<ShortcutInfo> assertAllNotHaveIcon(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertNull("ID " + s.getId(), s.getIcon());
+ }
+ return actualShortcuts;
+ }
+
+ @NonNull
+ protected List<ShortcutInfo> assertAllHaveFlags(@NonNull List<ShortcutInfo> actualShortcuts,
+ int shortcutFlags) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId() + " doesn't have flags " + shortcutFlags,
+ s.hasFlags(shortcutFlags));
+ }
+ return actualShortcuts;
+ }
+
+ protected ShortcutInfo getPackageShortcut(String packageName, String shortcutId, int userId) {
+ return mService.getPackageShortcutForTest(packageName, shortcutId, userId);
+ }
+
+ protected void assertShortcutExists(String packageName, String shortcutId, int userId) {
+ assertTrue(getPackageShortcut(packageName, shortcutId, userId) != null);
+ }
+
+ protected void assertShortcutNotExists(String packageName, String shortcutId, int userId) {
+ assertTrue(getPackageShortcut(packageName, shortcutId, userId) == null);
+ }
+
+ protected Intent[] launchShortcutAndGetIntentsInner(Runnable shortcutStarter,
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
+ reset(mMockActivityManagerInternal);
+ shortcutStarter.run();
+
+ final ArgumentCaptor<Intent[]> intentsCaptor = ArgumentCaptor.forClass(Intent[].class);
+ verify(mMockActivityManagerInternal).startActivitiesAsPackage(
+ eq(packageName),
+ eq(userId),
+ intentsCaptor.capture(),
+ any(Bundle.class));
+ return intentsCaptor.getValue();
+ }
+
+ protected Intent[] launchShortcutAndGetIntents(
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
+ return launchShortcutAndGetIntentsInner(
+ () -> {
+ mLauncherApps.startShortcut(packageName, shortcutId, null, null,
+ UserHandle.of(userId));
+ }, packageName, shortcutId, userId
+ );
+ }
+
+ protected Intent launchShortcutAndGetIntent(
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
+ final Intent[] intents = launchShortcutAndGetIntents(packageName, shortcutId, userId);
+ assertEquals(1, intents.length);
+ return intents[0];
+ }
+
+ protected Intent[] launchShortcutAndGetIntents_withShortcutInfo(
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
+ return launchShortcutAndGetIntentsInner(
+ () -> {
+ mLauncherApps.startShortcut(
+ getShortcutInfoAsLauncher(packageName, shortcutId, userId), null, null);
+ }, packageName, shortcutId, userId
+ );
+ }
+
+ protected Intent launchShortcutAndGetIntent_withShortcutInfo(
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
+ final Intent[] intents = launchShortcutAndGetIntents_withShortcutInfo(
+ packageName, shortcutId, userId);
+ assertEquals(1, intents.length);
+ return intents[0];
+ }
+
+ protected void assertShortcutLaunchable(@NonNull String packageName, @NonNull String shortcutId,
+ int userId) {
+ assertNotNull(launchShortcutAndGetIntent(packageName, shortcutId, userId));
+ assertNotNull(launchShortcutAndGetIntent_withShortcutInfo(packageName, shortcutId, userId));
+ }
+
+ protected void assertShortcutNotLaunched(@NonNull String packageName,
+ @NonNull String shortcutId, int userId) {
+ reset(mMockActivityManagerInternal);
+ try {
+ mLauncherApps.startShortcut(packageName, shortcutId, null, null,
+ UserHandle.of(userId));
+ fail("ActivityNotFoundException was not thrown");
+ } catch (ActivityNotFoundException expected) {
+ }
+ // This shouldn't have been called.
+ verify(mMockActivityManagerInternal, times(0)).startActivitiesAsPackage(
+ anyString(),
+ anyInt(),
+ any(Intent[].class),
+ any(Bundle.class));
+ }
+
+ protected void assertStartShortcutThrowsException(@NonNull String packageName,
+ @NonNull String shortcutId, int userId, Class<?> expectedException) {
+ Exception thrown = null;
+ try {
+ mLauncherApps.startShortcut(packageName, shortcutId, null, null,
+ UserHandle.of(userId));
+ } catch (Exception e) {
+ thrown = e;
+ }
+ assertNotNull("Exception was not thrown", thrown);
+ assertEquals("Exception type different", expectedException, thrown.getClass());
+ }
+
+ protected void assertBitmapDirectories(int userId, String... expectedDirectories) {
+ final Set<String> expected = hashSet(set(expectedDirectories));
+
+ final Set<String> actual = new HashSet<>();
+
+ final File[] files = mService.getUserBitmapFilePath(userId).listFiles();
+ if (files != null) {
+ for (File child : files) {
+ if (child.isDirectory()) {
+ actual.add(child.getName());
+ }
+ }
+ }
+
+ assertEquals(expected, actual);
+ }
+
+ protected void assertBitmapFiles(int userId, String packageName, String... expectedFiles) {
+ final Set<String> expected = hashSet(set(expectedFiles));
+
+ final Set<String> actual = new HashSet<>();
+
+ final File[] files = new File(mService.getUserBitmapFilePath(userId), packageName)
+ .listFiles();
+ if (files != null) {
+ for (File child : files) {
+ if (child.isFile()) {
+ actual.add(child.getName());
+ }
+ }
+ }
+
+ assertEquals(expected, actual);
+ }
+
+ protected String getBitmapFilename(int userId, String packageName, String shortcutId) {
+ final ShortcutInfo si = mService.getPackageShortcutForTest(packageName, shortcutId, userId);
+ if (si == null) {
+ return null;
+ }
+ return new File(si.getBitmapPath()).getName();
+ }
+
+ /**
+ * @return all shortcuts stored internally for the caller. This reflects the *internal* view
+ * of shortcuts, which may be different from what {@link #getCallerVisibleShortcuts} would
+ * return, because getCallerVisibleShortcuts() will get shortcuts from the proper "front door"
+ * which performs some extra checks, like {@link ShortcutPackage#onRestored}.
+ */
+ protected List<ShortcutInfo> getCallerShortcuts() {
+ final ShortcutPackage p = mService.getPackageShortcutForTest(
+ getCallingPackage(), getCallingUserId());
+ return p == null ? null : p.getAllShortcutsForTest();
+ }
+
+ /**
+ * @return all shortcuts owned by caller that are actually visible via ShortcutManager.
+ * See also {@link #getCallerShortcuts}.
+ */
+ protected List<ShortcutInfo> getCallerVisibleShortcuts() {
+ final ArrayList<ShortcutInfo> ret = new ArrayList<>();
+ ret.addAll(mManager.getDynamicShortcuts());
+ ret.addAll(mManager.getPinnedShortcuts());
+ ret.addAll(mManager.getManifestShortcuts());
+ return ret;
+ }
+
+ protected ShortcutInfo getCallerShortcut(String shortcutId) {
+ return getPackageShortcut(getCallingPackage(), shortcutId, getCallingUserId());
+ }
+
+ protected List<ShortcutInfo> getLauncherShortcuts(String launcher, int userId, int queryFlags) {
+ final List<ShortcutInfo>[] ret = new List[1];
+ runWithCaller(launcher, userId, () -> {
+ final ShortcutQuery q = new ShortcutQuery();
+ q.setQueryFlags(queryFlags);
+ ret[0] = mLauncherApps.getShortcuts(q, UserHandle.of(userId));
+ });
+ return ret[0];
+ }
+
+ protected List<ShortcutInfo> getLauncherPinnedShortcuts(String launcher, int userId) {
+ return getLauncherShortcuts(launcher, userId, ShortcutQuery.FLAG_GET_PINNED);
+ }
+
+ protected ShortcutInfo getShortcutInfoAsLauncher(String packageName, String shortcutId,
+ int userId) {
+ final List<ShortcutInfo> infoList =
+ mLauncherApps.getShortcutInfo(packageName, list(shortcutId),
+ UserHandle.of(userId));
+ assertEquals("No shortcutInfo found (or too many of them)", 1, infoList.size());
+ return infoList.get(0);
+ }
+
+ protected Intent genPackageAddIntent(String packageName, int userId) {
+ installPackage(userId, packageName);
+
+ Intent i = new Intent(Intent.ACTION_PACKAGE_ADDED);
+ i.setData(Uri.parse("package:" + packageName));
+ i.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ return i;
+ }
+
+ protected Intent genPackageDeleteIntent(String pakcageName, int userId) {
+ uninstallPackage(userId, pakcageName);
+
+ Intent i = new Intent(Intent.ACTION_PACKAGE_REMOVED);
+ i.setData(Uri.parse("package:" + pakcageName));
+ i.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ return i;
+ }
+
+ protected Intent genPackageUpdateIntent(String pakcageName, int userId) {
+ installPackage(userId, pakcageName);
+
+ Intent i = new Intent(Intent.ACTION_PACKAGE_ADDED);
+ i.setData(Uri.parse("package:" + pakcageName));
+ i.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ i.putExtra(Intent.EXTRA_REPLACING, true);
+ return i;
+ }
+
+ protected Intent genPackageChangedIntent(String pakcageName, int userId) {
+ Intent i = new Intent(Intent.ACTION_PACKAGE_CHANGED);
+ i.setData(Uri.parse("package:" + pakcageName));
+ i.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ return i;
+ }
+
+ protected Intent genPackageDataClear(String packageName, int userId) {
+ Intent i = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED);
+ i.setData(Uri.parse("package:" + packageName));
+ i.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ return i;
+ }
+
+ protected void assertExistsAndShadow(ShortcutPackageItem spi) {
+ assertNotNull(spi);
+ assertTrue(spi.getPackageInfo().isShadow());
+ }
+
+ protected File makeFile(File baseDirectory, String... paths) {
+ File ret = baseDirectory;
+
+ for (String path : paths) {
+ ret = new File(ret, path);
+ }
+
+ return ret;
+ }
+
+ protected boolean bitmapDirectoryExists(String packageName, int userId) {
+ final File path = new File(mService.getUserBitmapFilePath(userId), packageName);
+ return path.isDirectory();
+ }
+ protected static ShortcutQuery buildQuery(long changedSince,
+ String packageName, ComponentName componentName,
+ /* @ShortcutQuery.QueryFlags */ int flags) {
+ return buildQuery(changedSince, packageName, null, componentName, flags);
+ }
+
+ protected static ShortcutQuery buildQuery(long changedSince,
+ String packageName, List<String> shortcutIds, ComponentName componentName,
+ /* @ShortcutQuery.QueryFlags */ int flags) {
+ final ShortcutQuery q = new ShortcutQuery();
+ q.setChangedSince(changedSince);
+ q.setPackage(packageName);
+ q.setShortcutIds(shortcutIds);
+ q.setActivity(componentName);
+ q.setQueryFlags(flags);
+ return q;
+ }
+
+ protected static ShortcutQuery buildAllQuery(String packageName) {
+ final ShortcutQuery q = new ShortcutQuery();
+ q.setPackage(packageName);
+ q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS);
+ return q;
+ }
+
+ protected static ShortcutQuery buildPinnedQuery(String packageName) {
+ final ShortcutQuery q = new ShortcutQuery();
+ q.setPackage(packageName);
+ q.setQueryFlags(ShortcutQuery.FLAG_GET_PINNED);
+ return q;
+ }
+
+ protected static ShortcutQuery buildQueryWithFlags(int queryFlags) {
+ final ShortcutQuery q = new ShortcutQuery();
+ q.setQueryFlags(queryFlags);
+ return q;
+ }
+
+ protected void backupAndRestore() {
+ int prevUid = mInjectedCallingUid;
+
+ mInjectedCallingUid = Process.SYSTEM_UID; // Only system can call it.
+
+ dumpsysOnLogcat("Before backup");
+
+ final byte[] payload = mService.getBackupPayload(USER_0);
+ if (ENABLE_DUMP) {
+ final String xml = new String(payload);
+ Log.v(TAG, "Backup payload:");
+ for (String line : xml.split("\n")) {
+ Log.v(TAG, line);
+ }
+ }
+
+ // Before doing anything else, uninstall all packages.
+ for (int userId : list(USER_0, USER_P0)) {
+ for (String pkg : list(CALLING_PACKAGE_1, CALLING_PACKAGE_2, CALLING_PACKAGE_3,
+ LAUNCHER_1, LAUNCHER_2, LAUNCHER_3)) {
+ uninstallPackage(userId, pkg);
+ }
+ }
+
+ shutdownServices();
+
+ deleteAllSavedFiles();
+
+ initService();
+ mService.applyRestore(payload, USER_0);
+
+ // handleUnlockUser will perform the gone package check, but it shouldn't remove
+ // shadow information.
+ mService.handleUnlockUser(USER_0);
+
+ dumpsysOnLogcat("After restore");
+
+ mInjectedCallingUid = prevUid;
+ }
+
+ protected void prepareCrossProfileDataSet() {
+ mRunningUsers.put(USER_10, true); // this test needs user 10.
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"),
+ makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6"))));
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"),
+ makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6"))));
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"),
+ makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6"))));
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list()));
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"),
+ makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6"))));
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("x1"), makeShortcut("x2"), makeShortcut("x3"),
+ makeShortcut("x4"), makeShortcut("x5"), makeShortcut("x6"))));
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s1", "s2"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("s1", "s2", "s3"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1", "s4"), HANDLE_USER_P0);
+ });
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s2", "s3"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("s2", "s3", "s4"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2", "s5"), HANDLE_USER_P0);
+ });
+
+ // Note LAUNCHER_3 has allowBackup=false.
+ runWithCaller(LAUNCHER_3, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s3", "s4"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("s3", "s4", "s5"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3", "s6"), HANDLE_USER_P0);
+ });
+ runWithCaller(LAUNCHER_4, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list(), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list(), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list(), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_4, list(), HANDLE_USER_0);
+ });
+
+ // Launcher on a managed profile is referring ot user 0!
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3", "s4"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s3", "s4", "s5"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("s3", "s4", "s5", "s6"),
+ HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s4", "s1"), HANDLE_USER_P0);
+ });
+ runWithCaller(LAUNCHER_1, USER_10, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("x4", "x5"), HANDLE_USER_10);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("x4", "x5", "x6"), HANDLE_USER_10);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("x4", "x5", "x6", "x1"),
+ HANDLE_USER_10);
+ });
+
+ // Then remove some dynamic shortcuts.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list()));
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("x1"), makeShortcut("x2"), makeShortcut("x3"))));
+ });
+ }
+
+ public static List<ShortcutInfo> assertAllHaveIconResId(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId() + " not have icon res ID", s.hasIconResource());
+ assertFalse("ID " + s.getId() + " shouldn't have icon FD", s.hasIconFile());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllHaveIconFile(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertFalse("ID " + s.getId() + " shouldn't have icon res ID", s.hasIconResource());
+ assertTrue("ID " + s.getId() + " not have icon FD", s.hasIconFile());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllHaveIcon(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId() + " has no icon ", s.hasIconFile() || s.hasIconResource());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllStringsResolved(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), s.hasStringResourcesResolved());
+ }
+ return actualShortcuts;
+ }
+
+ public String readTestAsset(String assetPath) throws IOException {
+ final StringBuilder sb = new StringBuilder();
+ try (BufferedReader br = new BufferedReader(
+ new InputStreamReader(
+ getTestContext().getResources().getAssets().open(assetPath)))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ sb.append(line);
+ sb.append(System.lineSeparator());
+ }
+ }
+ return sb.toString();
+ }
+
+ protected void prepareGetHomeActivitiesAsUser(ComponentName preferred,
+ List<ResolveInfo> candidates, int userId) {
+ doAnswer(inv -> {
+ ((List) inv.getArguments()[0]).addAll(candidates);
+ return preferred;
+ }).when(mMockPackageManagerInternal).getHomeActivitiesAsUser(any(List.class), eq(userId));
+ }
+
+ protected static ComponentName cn(String packageName, String name) {
+ return new ComponentName(packageName, name);
+ }
+
+ protected static ResolveInfo ri(String packageName, String name, boolean isSystem, int priority) {
+ final ResolveInfo ri = new ResolveInfo();
+ ri.activityInfo = new ActivityInfo();
+ ri.activityInfo.applicationInfo = new ApplicationInfo();
+
+ ri.activityInfo.packageName = packageName;
+ ri.activityInfo.name = name;
+ if (isSystem) {
+ ri.activityInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ }
+ ri.priority = priority;
+ return ri;
+ }
+
+ protected static ResolveInfo getSystemLauncher() {
+ return ri(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME, true,
+ PACKAGE_SYSTEM_LAUNCHER_PRIORITY);
+ }
+
+ protected static ResolveInfo getFallbackLauncher() {
+ return ri(PACKAGE_FALLBACK_LAUNCHER, PACKAGE_FALLBACK_LAUNCHER_NAME, true,
+ PACKAGE_FALLBACK_LAUNCHER_PRIORITY);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
new file mode 100644
index 0000000..253334e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -0,0 +1,7159 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDisabled;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDynamic;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDynamicOrPinned;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllEnabled;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllHaveIntents;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllHaveTitle;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllImmutable;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllKeyFieldsOnly;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllManifest;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllNotHaveIntents;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllNotHaveTitle;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllNotKeyFieldsOnly;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllNotManifest;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllPinned;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllUnique;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertBitmapSize;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertBundleEmpty;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertCallbackNotReceived;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertCallbackReceived;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertCannotUpdateImmutable;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertDynamicAndPinned;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertDynamicOnly;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertDynamicShortcutCountExceeded;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertEmpty;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertForLauncherCallback;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertShortcutIds;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.filterByActivity;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.findShortcut;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.hashSet;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.makeBundle;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.pfdToBitmap;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.resetAll;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.waitOnMainThread;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.Manifest.permission;
+import android.app.ActivityManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Process;
+import android.os.UserHandle;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.frameworks.servicestests.R;
+import com.android.server.pm.ShortcutService.ConfigConstants;
+import com.android.server.pm.ShortcutService.FileOutputStreamWithPath;
+import com.android.server.pm.ShortcutUser.PackageWithUser;
+
+import org.mockito.ArgumentCaptor;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Tests for ShortcutService and ShortcutManager.
+ *
+ m FrameworksServicesTests &&
+ adb install \
+ -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+ adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest1 \
+ -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ */
+@SmallTest
+public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
+
+ /**
+ * Test for the first launch path, no settings file available.
+ */
+ public void testFirstInitialize() {
+ assertResetTimes(START_TIME, START_TIME + INTERVAL);
+ }
+
+ /**
+ * Test for {@link ShortcutService#getLastResetTimeLocked()} and
+ * {@link ShortcutService#getNextResetTimeLocked()}.
+ */
+ public void testUpdateAndGetNextResetTimeLocked() {
+ assertResetTimes(START_TIME, START_TIME + INTERVAL);
+
+ // Advance clock.
+ mInjectedCurrentTimeMillis += 100;
+
+ // Shouldn't have changed.
+ assertResetTimes(START_TIME, START_TIME + INTERVAL);
+
+ // Advance clock, almost the reset time.
+ mInjectedCurrentTimeMillis = START_TIME + INTERVAL - 1;
+
+ // Shouldn't have changed.
+ assertResetTimes(START_TIME, START_TIME + INTERVAL);
+
+ // Advance clock.
+ mInjectedCurrentTimeMillis += 1;
+
+ assertResetTimes(START_TIME + INTERVAL, START_TIME + 2 * INTERVAL);
+
+ // Advance further; 4 hours since start.
+ mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL + 50;
+
+ assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL);
+ }
+
+ /**
+ * Test for the restoration from saved file.
+ */
+ public void testInitializeFromSavedFile() {
+
+ mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL + 50;
+ assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL);
+
+ mService.saveBaseStateLocked();
+
+ dumpBaseStateFile();
+
+ mService.saveDirtyInfo();
+
+ // Restore.
+ initService();
+
+ assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL);
+ }
+
+ /**
+ * Test for the restoration from restored file.
+ */
+ public void testLoadFromBrokenFile() {
+ // TODO Add various broken cases.
+ }
+
+ public void testLoadConfig() {
+ mService.updateConfigurationLocked(
+ ConfigConstants.KEY_RESET_INTERVAL_SEC + "=123,"
+ + ConfigConstants.KEY_MAX_SHORTCUTS + "=4,"
+ + ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL + "=5,"
+ + ConfigConstants.KEY_MAX_ICON_DIMENSION_DP + "=100,"
+ + ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM + "=50,"
+ + ConfigConstants.KEY_ICON_FORMAT + "=WEBP,"
+ + ConfigConstants.KEY_ICON_QUALITY + "=75");
+ assertEquals(123000, mService.getResetIntervalForTest());
+ assertEquals(4, mService.getMaxShortcutsForTest());
+ assertEquals(5, mService.getMaxUpdatesPerIntervalForTest());
+ assertEquals(100, mService.getMaxIconDimensionForTest());
+ assertEquals(CompressFormat.WEBP, mService.getIconPersistFormatForTest());
+ assertEquals(75, mService.getIconPersistQualityForTest());
+
+ mInjectedIsLowRamDevice = true;
+ mService.updateConfigurationLocked(
+ ConfigConstants.KEY_MAX_ICON_DIMENSION_DP + "=100,"
+ + ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM + "=50,"
+ + ConfigConstants.KEY_ICON_FORMAT + "=JPEG");
+ assertEquals(ShortcutService.DEFAULT_RESET_INTERVAL_SEC * 1000,
+ mService.getResetIntervalForTest());
+
+ assertEquals(ShortcutService.DEFAULT_MAX_SHORTCUTS_PER_APP,
+ mService.getMaxShortcutsForTest());
+
+ assertEquals(ShortcutService.DEFAULT_MAX_UPDATES_PER_INTERVAL,
+ mService.getMaxUpdatesPerIntervalForTest());
+
+ assertEquals(50, mService.getMaxIconDimensionForTest());
+
+ assertEquals(CompressFormat.JPEG, mService.getIconPersistFormatForTest());
+
+ assertEquals(ShortcutService.DEFAULT_ICON_PERSIST_QUALITY,
+ mService.getIconPersistQualityForTest());
+ }
+
+ // === Test for app side APIs ===
+
+ /** Test for {@link android.content.pm.ShortcutManager#getMaxShortcutCountForActivity()} */
+ public void testGetMaxDynamicShortcutCount() {
+ assertEquals(MAX_SHORTCUTS, mManager.getMaxShortcutCountForActivity());
+ }
+
+ /** Test for {@link android.content.pm.ShortcutManager#getRemainingCallCount()} */
+ public void testGetRemainingCallCount() {
+ assertEquals(MAX_UPDATES_PER_INTERVAL, mManager.getRemainingCallCount());
+ }
+
+ public void testGetIconMaxDimensions() {
+ assertEquals(MAX_ICON_DIMENSION, mManager.getIconMaxWidth());
+ assertEquals(MAX_ICON_DIMENSION, mManager.getIconMaxHeight());
+ }
+
+ /** Test for {@link android.content.pm.ShortcutManager#getRateLimitResetTime()} */
+ public void testGetRateLimitResetTime() {
+ assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+
+ mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL + 50;
+
+ assertEquals(START_TIME + 5 * INTERVAL, mManager.getRateLimitResetTime());
+ }
+
+ public void testSetDynamicShortcuts() {
+ setCaller(CALLING_PACKAGE_1, USER_0);
+
+ final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.icon1);
+ final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.icon2));
+
+ final ShortcutInfo si1 = makeShortcut(
+ "shortcut1",
+ "Title 1",
+ makeComponent(ShortcutActivity.class),
+ icon1,
+ makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
+ "key1", "val1", "nest", makeBundle("key", 123)),
+ /* weight */ 10);
+
+ final ShortcutInfo si2 = makeShortcut(
+ "shortcut2",
+ "Title 2",
+ /* activity */ null,
+ icon2,
+ makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
+ /* weight */ 12);
+ final ShortcutInfo si3 = makeShortcut("shortcut3");
+
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2)));
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut1", "shortcut2");
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ // TODO: Check fields
+
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut1");
+ assertEquals(1, mManager.getRemainingCallCount());
+
+ assertTrue(mManager.setDynamicShortcuts(list()));
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertEquals(0, mManager.getRemainingCallCount());
+
+ dumpsysOnLogcat();
+
+ mInjectedCurrentTimeMillis++; // Need to advance the clock for reset to work.
+ mService.resetThrottlingInner(UserHandle.USER_SYSTEM);
+
+ dumpsysOnLogcat();
+
+ assertTrue(mManager.setDynamicShortcuts(list(si2, si3)));
+ assertEquals(2, mManager.getDynamicShortcuts().size());
+
+ // TODO Check max number
+
+ mRunningUsers.put(USER_10, true);
+
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))));
+ });
+ }
+
+ public void testAddDynamicShortcuts() {
+ setCaller(CALLING_PACKAGE_1, USER_0);
+
+ final ShortcutInfo si1 = makeShortcut("shortcut1");
+ final ShortcutInfo si2 = makeShortcut("shortcut2");
+ final ShortcutInfo si3 = makeShortcut("shortcut3");
+
+ assertEquals(3, mManager.getRemainingCallCount());
+
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertEquals(2, mManager.getRemainingCallCount());
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut1");
+
+ assertTrue(mManager.addDynamicShortcuts(list(si2, si3)));
+ assertEquals(1, mManager.getRemainingCallCount());
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut1", "shortcut2", "shortcut3");
+
+ // This should not crash. It'll still consume the quota.
+ assertTrue(mManager.addDynamicShortcuts(list()));
+ assertEquals(0, mManager.getRemainingCallCount());
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut1", "shortcut2", "shortcut3");
+
+ mInjectedCurrentTimeMillis += INTERVAL; // reset
+
+ // Add with the same ID
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("shortcut1"))));
+ assertEquals(2, mManager.getRemainingCallCount());
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut1", "shortcut2", "shortcut3");
+
+ // TODO Check max number
+
+ // TODO Check fields.
+
+ mRunningUsers.put(USER_10, true);
+
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
+ });
+ }
+
+ public void testPublishWithNoActivity() {
+ // If activity is not explicitly set, use the default one.
+
+ mRunningUsers.put(USER_10, true);
+
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ // s1 and s3 has no activities.
+ final ShortcutInfo si1 = new ShortcutInfo.Builder(mClientContext, "si1")
+ .setShortLabel("label1")
+ .setIntent(new Intent("action1"))
+ .build();
+ final ShortcutInfo si2 = new ShortcutInfo.Builder(mClientContext, "si2")
+ .setShortLabel("label2")
+ .setActivity(new ComponentName(getCallingPackage(), "abc"))
+ .setIntent(new Intent("action2"))
+ .build();
+ final ShortcutInfo si3 = new ShortcutInfo.Builder(mClientContext, "si3")
+ .setShortLabel("label3")
+ .setIntent(new Intent("action3"))
+ .build();
+
+ // Set test 1
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+
+ assertWith(getCallerShortcuts())
+ .haveIds("si1")
+ .forShortcutWithId("si1", si -> {
+ assertEquals(new ComponentName(getCallingPackage(),
+ MAIN_ACTIVITY_CLASS), si.getActivity());
+ });
+
+ // Set test 2
+ assertTrue(mManager.setDynamicShortcuts(list(si2, si1)));
+
+ assertWith(getCallerShortcuts())
+ .haveIds("si1", "si2")
+ .forShortcutWithId("si1", si -> {
+ assertEquals(new ComponentName(getCallingPackage(),
+ MAIN_ACTIVITY_CLASS), si.getActivity());
+ })
+ .forShortcutWithId("si2", si -> {
+ assertEquals(new ComponentName(getCallingPackage(),
+ "abc"), si.getActivity());
+ });
+
+
+ // Set test 3
+ assertTrue(mManager.setDynamicShortcuts(list(si3, si1)));
+
+ assertWith(getCallerShortcuts())
+ .haveIds("si1", "si3")
+ .forShortcutWithId("si1", si -> {
+ assertEquals(new ComponentName(getCallingPackage(),
+ MAIN_ACTIVITY_CLASS), si.getActivity());
+ })
+ .forShortcutWithId("si3", si -> {
+ assertEquals(new ComponentName(getCallingPackage(),
+ MAIN_ACTIVITY_CLASS), si.getActivity());
+ });
+
+ mInjectedCurrentTimeMillis += INTERVAL; // reset throttling
+
+ // Add test 1
+ mManager.removeAllDynamicShortcuts();
+ assertTrue(mManager.addDynamicShortcuts(list(si1)));
+
+ assertWith(getCallerShortcuts())
+ .haveIds("si1")
+ .forShortcutWithId("si1", si -> {
+ assertEquals(new ComponentName(getCallingPackage(),
+ MAIN_ACTIVITY_CLASS), si.getActivity());
+ });
+
+ // Add test 2
+ mManager.removeAllDynamicShortcuts();
+ assertTrue(mManager.addDynamicShortcuts(list(si2, si1)));
+
+ assertWith(getCallerShortcuts())
+ .haveIds("si1", "si2")
+ .forShortcutWithId("si1", si -> {
+ assertEquals(new ComponentName(getCallingPackage(),
+ MAIN_ACTIVITY_CLASS), si.getActivity());
+ })
+ .forShortcutWithId("si2", si -> {
+ assertEquals(new ComponentName(getCallingPackage(),
+ "abc"), si.getActivity());
+ });
+
+
+ // Add test 3
+ mManager.removeAllDynamicShortcuts();
+ assertTrue(mManager.addDynamicShortcuts(list(si3, si1)));
+
+ assertWith(getCallerShortcuts())
+ .haveIds("si1", "si3")
+ .forShortcutWithId("si1", si -> {
+ assertEquals(new ComponentName(getCallingPackage(),
+ MAIN_ACTIVITY_CLASS), si.getActivity());
+ })
+ .forShortcutWithId("si3", si -> {
+ assertEquals(new ComponentName(getCallingPackage(),
+ MAIN_ACTIVITY_CLASS), si.getActivity());
+ });
+ });
+ }
+
+ public void testPublishWithNoActivity_noMainActivityInPackage() {
+ mRunningUsers.put(USER_10, true);
+
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ final ShortcutInfo si1 = new ShortcutInfo.Builder(mClientContext, "si1")
+ .setShortLabel("label1")
+ .setIntent(new Intent("action1"))
+ .build();
+
+ // Returning null means there's no main activity in this package.
+ mMainActivityFetcher = (packageName, userId) -> null;
+
+ assertExpectException(
+ RuntimeException.class, "Launcher activity not found for", () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ });
+ });
+ }
+
+ public void testDeleteDynamicShortcuts() {
+ final ShortcutInfo si1 = makeShortcut("shortcut1");
+ final ShortcutInfo si2 = makeShortcut("shortcut2");
+ final ShortcutInfo si3 = makeShortcut("shortcut3");
+ final ShortcutInfo si4 = makeShortcut("shortcut4");
+
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2, si3, si4)));
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut1", "shortcut2", "shortcut3", "shortcut4");
+
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ mManager.removeDynamicShortcuts(list("shortcut1"));
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut2", "shortcut3", "shortcut4");
+
+ mManager.removeDynamicShortcuts(list("shortcut1"));
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut2", "shortcut3", "shortcut4");
+
+ mManager.removeDynamicShortcuts(list("shortcutXXX"));
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut2", "shortcut3", "shortcut4");
+
+ mManager.removeDynamicShortcuts(list("shortcut2", "shortcut4"));
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut3");
+
+ mManager.removeDynamicShortcuts(list("shortcut3"));
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()));
+
+ // Still 2 calls left.
+ assertEquals(2, mManager.getRemainingCallCount());
+ }
+
+ public void testDeleteAllDynamicShortcuts() {
+ final ShortcutInfo si1 = makeShortcut("shortcut1");
+ final ShortcutInfo si2 = makeShortcut("shortcut2");
+ final ShortcutInfo si3 = makeShortcut("shortcut3");
+
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2, si3)));
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut1", "shortcut2", "shortcut3");
+
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ mManager.removeAllDynamicShortcuts();
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ // Note delete shouldn't affect throttling, so...
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+
+ // This should still work.
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2, si3)));
+ assertEquals(3, mManager.getDynamicShortcuts().size());
+
+ // Still 1 call left
+ assertEquals(1, mManager.getRemainingCallCount());
+ }
+
+ public void testIcons() throws IOException {
+ final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
+ final Icon res64x64 = Icon.createWithResource(getTestContext(), R.drawable.black_64x64);
+ final Icon res512x512 = Icon.createWithResource(getTestContext(), R.drawable.black_512x512);
+
+ final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.black_32x32));
+ final Icon bmp64x64 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.black_64x64));
+ final Icon bmp512x512 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.black_512x512));
+
+ // Set from package 1
+ setCaller(CALLING_PACKAGE_1);
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcutWithIcon("res32x32", res32x32),
+ makeShortcutWithIcon("res64x64", res64x64),
+ makeShortcutWithIcon("bmp32x32", bmp32x32),
+ makeShortcutWithIcon("bmp64x64", bmp64x64),
+ makeShortcutWithIcon("bmp512x512", bmp512x512),
+ makeShortcut("none")
+ )));
+
+ // getDynamicShortcuts() shouldn't return icons, thus assertAllNotHaveIcon().
+ assertShortcutIds(assertAllNotHaveIcon(mManager.getDynamicShortcuts()),
+ "res32x32",
+ "res64x64",
+ "bmp32x32",
+ "bmp64x64",
+ "bmp512x512",
+ "none");
+
+ // Call from another caller with the same ID, just to make sure storage is per-package.
+ setCaller(CALLING_PACKAGE_2);
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcutWithIcon("res32x32", res512x512),
+ makeShortcutWithIcon("res64x64", res512x512),
+ makeShortcutWithIcon("none", res512x512)
+ )));
+ assertShortcutIds(assertAllNotHaveIcon(mManager.getDynamicShortcuts()),
+ "res32x32",
+ "res64x64",
+ "none");
+
+ // Different profile. Note the names and the contents don't match.
+ setCaller(CALLING_PACKAGE_1, USER_P0);
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcutWithIcon("res32x32", res512x512),
+ makeShortcutWithIcon("bmp32x32", bmp512x512)
+ )));
+ assertShortcutIds(assertAllNotHaveIcon(mManager.getDynamicShortcuts()),
+ "res32x32",
+ "bmp32x32");
+
+ // Re-initialize and load from the files.
+ mService.saveDirtyInfo();
+ initService();
+
+ // Load from launcher.
+ Bitmap bmp;
+
+ setCaller(LAUNCHER_1);
+ // Check hasIconResource()/hasIconFile().
+ assertShortcutIds(assertAllHaveIconResId(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_0))),
+ "res32x32");
+
+ assertShortcutIds(assertAllHaveIconResId(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res64x64", USER_0))),
+ "res64x64");
+
+ assertShortcutIds(assertAllHaveIconFile(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_0))),
+ "bmp32x32");
+
+ assertShortcutIds(assertAllHaveIconFile(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp64x64", USER_0))),
+ "bmp64x64");
+
+ assertShortcutIds(assertAllHaveIconFile(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp512x512", USER_0))),
+ "bmp512x512");
+
+ assertShortcutIds(assertAllHaveIconResId(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_P0))),
+ "res32x32");
+ assertShortcutIds(assertAllHaveIconFile(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_P0))),
+ "bmp32x32");
+
+ // Check
+ assertEquals(
+ R.drawable.black_32x32,
+ mLauncherApps.getShortcutIconResId(
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_0)));
+
+ assertEquals(
+ R.drawable.black_64x64,
+ mLauncherApps.getShortcutIconResId(
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res64x64", USER_0)));
+
+ assertEquals(
+ 0, // because it's not a resource
+ mLauncherApps.getShortcutIconResId(
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_0)));
+ assertEquals(
+ 0, // because it's not a resource
+ mLauncherApps.getShortcutIconResId(
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp64x64", USER_0)));
+ assertEquals(
+ 0, // because it's not a resource
+ mLauncherApps.getShortcutIconResId(
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp512x512", USER_0)));
+
+ bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_0)));
+ assertBitmapSize(32, 32, bmp);
+
+ bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp64x64", USER_0)));
+ assertBitmapSize(64, 64, bmp);
+
+ bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp512x512", USER_0)));
+ assertBitmapSize(128, 128, bmp);
+
+ assertEquals(
+ R.drawable.black_512x512,
+ mLauncherApps.getShortcutIconResId(
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_P0)));
+ // Should be 512x512, so shrunk.
+ bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_P0)));
+ assertBitmapSize(128, 128, bmp);
+
+ // Also check the overload APIs too.
+ assertEquals(
+ R.drawable.black_32x32,
+ mLauncherApps.getShortcutIconResId(CALLING_PACKAGE_1, "res32x32", HANDLE_USER_0));
+ assertEquals(
+ R.drawable.black_64x64,
+ mLauncherApps.getShortcutIconResId(CALLING_PACKAGE_1, "res64x64", HANDLE_USER_0));
+ assertEquals(
+ R.drawable.black_512x512,
+ mLauncherApps.getShortcutIconResId(CALLING_PACKAGE_1, "res32x32", HANDLE_USER_P0));
+ bmp = pfdToBitmap(
+ mLauncherApps.getShortcutIconFd(CALLING_PACKAGE_1, "bmp32x32", HANDLE_USER_P0));
+ assertBitmapSize(128, 128, bmp);
+ }
+
+ public void testCleanupDanglingBitmaps() throws Exception {
+ assertBitmapDirectories(USER_0, EMPTY_STRINGS);
+ assertBitmapDirectories(USER_10, EMPTY_STRINGS);
+
+ // Make some shortcuts with bitmap icons.
+ final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.black_32x32));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.setDynamicShortcuts(list(
+ makeShortcutWithIcon("s1", bmp32x32),
+ makeShortcutWithIcon("s2", bmp32x32),
+ makeShortcutWithIcon("s3", bmp32x32)
+ ));
+ });
+
+ // Increment the time (which actually we don't have to), which is used for filenames.
+ mInjectedCurrentTimeMillis++;
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ mManager.setDynamicShortcuts(list(
+ makeShortcutWithIcon("s4", bmp32x32),
+ makeShortcutWithIcon("s5", bmp32x32),
+ makeShortcutWithIcon("s6", bmp32x32)
+ ));
+ });
+
+ // Increment the time, which is used for filenames.
+ mInjectedCurrentTimeMillis++;
+
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ mManager.setDynamicShortcuts(list(
+ ));
+ });
+
+ // For USER-10, let's try without updating the times.
+ mRunningUsers.put(USER_10, true);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ mManager.setDynamicShortcuts(list(
+ makeShortcutWithIcon("10s1", bmp32x32),
+ makeShortcutWithIcon("10s2", bmp32x32),
+ makeShortcutWithIcon("10s3", bmp32x32)
+ ));
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ mManager.setDynamicShortcuts(list(
+ makeShortcutWithIcon("10s4", bmp32x32),
+ makeShortcutWithIcon("10s5", bmp32x32),
+ makeShortcutWithIcon("10s6", bmp32x32)
+ ));
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_10, () -> {
+ mManager.setDynamicShortcuts(list(
+ ));
+ });
+
+ dumpsysOnLogcat();
+
+ // Check files and directories.
+ // Package 3 has no bitmaps, so we don't create a directory.
+ assertBitmapDirectories(USER_0, CALLING_PACKAGE_1, CALLING_PACKAGE_2);
+ assertBitmapDirectories(USER_10, CALLING_PACKAGE_1, CALLING_PACKAGE_2);
+
+ assertBitmapFiles(USER_0, CALLING_PACKAGE_1,
+ getBitmapFilename(USER_0, CALLING_PACKAGE_1, "s1"),
+ getBitmapFilename(USER_0, CALLING_PACKAGE_1, "s2"),
+ getBitmapFilename(USER_0, CALLING_PACKAGE_1, "s3")
+ );
+ assertBitmapFiles(USER_0, CALLING_PACKAGE_2,
+ getBitmapFilename(USER_0, CALLING_PACKAGE_2, "s4"),
+ getBitmapFilename(USER_0, CALLING_PACKAGE_2, "s5"),
+ getBitmapFilename(USER_0, CALLING_PACKAGE_2, "s6")
+ );
+ assertBitmapFiles(USER_0, CALLING_PACKAGE_3,
+ EMPTY_STRINGS
+ );
+ assertBitmapFiles(USER_10, CALLING_PACKAGE_1,
+ getBitmapFilename(USER_10, CALLING_PACKAGE_1, "10s1"),
+ getBitmapFilename(USER_10, CALLING_PACKAGE_1, "10s2"),
+ getBitmapFilename(USER_10, CALLING_PACKAGE_1, "10s3")
+ );
+ assertBitmapFiles(USER_10, CALLING_PACKAGE_2,
+ getBitmapFilename(USER_10, CALLING_PACKAGE_2, "10s4"),
+ getBitmapFilename(USER_10, CALLING_PACKAGE_2, "10s5"),
+ getBitmapFilename(USER_10, CALLING_PACKAGE_2, "10s6")
+ );
+ assertBitmapFiles(USER_10, CALLING_PACKAGE_3,
+ EMPTY_STRINGS
+ );
+
+ // Then create random directories and files.
+ makeFile(mService.getUserBitmapFilePath(USER_0), "a.b.c").mkdir();
+ makeFile(mService.getUserBitmapFilePath(USER_0), "d.e.f").mkdir();
+ makeFile(mService.getUserBitmapFilePath(USER_0), "d.e.f", "123").createNewFile();
+ makeFile(mService.getUserBitmapFilePath(USER_0), "d.e.f", "456").createNewFile();
+
+ makeFile(mService.getUserBitmapFilePath(USER_0), CALLING_PACKAGE_3).mkdir();
+
+ makeFile(mService.getUserBitmapFilePath(USER_0), CALLING_PACKAGE_1, "1").createNewFile();
+ makeFile(mService.getUserBitmapFilePath(USER_0), CALLING_PACKAGE_1, "2").createNewFile();
+ makeFile(mService.getUserBitmapFilePath(USER_0), CALLING_PACKAGE_1, "3").createNewFile();
+ makeFile(mService.getUserBitmapFilePath(USER_0), CALLING_PACKAGE_1, "4").createNewFile();
+
+ makeFile(mService.getUserBitmapFilePath(USER_10), "10a.b.c").mkdir();
+ makeFile(mService.getUserBitmapFilePath(USER_10), "10d.e.f").mkdir();
+ makeFile(mService.getUserBitmapFilePath(USER_10), "10d.e.f", "123").createNewFile();
+ makeFile(mService.getUserBitmapFilePath(USER_10), "10d.e.f", "456").createNewFile();
+
+ makeFile(mService.getUserBitmapFilePath(USER_10), CALLING_PACKAGE_2, "1").createNewFile();
+ makeFile(mService.getUserBitmapFilePath(USER_10), CALLING_PACKAGE_2, "2").createNewFile();
+ makeFile(mService.getUserBitmapFilePath(USER_10), CALLING_PACKAGE_2, "3").createNewFile();
+ makeFile(mService.getUserBitmapFilePath(USER_10), CALLING_PACKAGE_2, "4").createNewFile();
+
+ assertBitmapDirectories(USER_0, CALLING_PACKAGE_1, CALLING_PACKAGE_2, CALLING_PACKAGE_3,
+ "a.b.c", "d.e.f");
+
+ // Save and load. When a user is loaded, we do the cleanup.
+ mService.saveDirtyInfo();
+ initService();
+
+ mService.handleUnlockUser(USER_0);
+ mService.handleUnlockUser(USER_10);
+ mService.handleUnlockUser(20); // Make sure the logic will still work for nonexistent user.
+
+ // The below check is the same as above, except this time USER_0 use the CALLING_PACKAGE_3
+ // directory.
+
+ assertBitmapDirectories(USER_0, CALLING_PACKAGE_1, CALLING_PACKAGE_2, CALLING_PACKAGE_3);
+ assertBitmapDirectories(USER_10, CALLING_PACKAGE_1, CALLING_PACKAGE_2);
+
+ assertBitmapFiles(USER_0, CALLING_PACKAGE_1,
+ getBitmapFilename(USER_0, CALLING_PACKAGE_1, "s1"),
+ getBitmapFilename(USER_0, CALLING_PACKAGE_1, "s2"),
+ getBitmapFilename(USER_0, CALLING_PACKAGE_1, "s3")
+ );
+ assertBitmapFiles(USER_0, CALLING_PACKAGE_2,
+ getBitmapFilename(USER_0, CALLING_PACKAGE_2, "s4"),
+ getBitmapFilename(USER_0, CALLING_PACKAGE_2, "s5"),
+ getBitmapFilename(USER_0, CALLING_PACKAGE_2, "s6")
+ );
+ assertBitmapFiles(USER_0, CALLING_PACKAGE_3,
+ EMPTY_STRINGS
+ );
+ assertBitmapFiles(USER_10, CALLING_PACKAGE_1,
+ getBitmapFilename(USER_10, CALLING_PACKAGE_1, "10s1"),
+ getBitmapFilename(USER_10, CALLING_PACKAGE_1, "10s2"),
+ getBitmapFilename(USER_10, CALLING_PACKAGE_1, "10s3")
+ );
+ assertBitmapFiles(USER_10, CALLING_PACKAGE_2,
+ getBitmapFilename(USER_10, CALLING_PACKAGE_2, "10s4"),
+ getBitmapFilename(USER_10, CALLING_PACKAGE_2, "10s5"),
+ getBitmapFilename(USER_10, CALLING_PACKAGE_2, "10s6")
+ );
+ assertBitmapFiles(USER_10, CALLING_PACKAGE_3,
+ EMPTY_STRINGS
+ );
+ }
+
+ protected void checkShrinkBitmap(int expectedWidth, int expectedHeight, int resId, int maxSize) {
+ assertBitmapSize(expectedWidth, expectedHeight,
+ ShortcutService.shrinkBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), resId),
+ maxSize));
+ }
+
+ public void testShrinkBitmap() {
+ checkShrinkBitmap(32, 32, R.drawable.black_512x512, 32);
+ checkShrinkBitmap(511, 511, R.drawable.black_512x512, 511);
+ checkShrinkBitmap(512, 512, R.drawable.black_512x512, 512);
+
+ checkShrinkBitmap(1024, 4096, R.drawable.black_1024x4096, 4096);
+ checkShrinkBitmap(1024, 4096, R.drawable.black_1024x4096, 4100);
+ checkShrinkBitmap(512, 2048, R.drawable.black_1024x4096, 2048);
+
+ checkShrinkBitmap(4096, 1024, R.drawable.black_4096x1024, 4096);
+ checkShrinkBitmap(4096, 1024, R.drawable.black_4096x1024, 4100);
+ checkShrinkBitmap(2048, 512, R.drawable.black_4096x1024, 2048);
+ }
+
+ protected File openIconFileForWriteAndGetPath(int userId, String packageName)
+ throws IOException {
+ // Shortcut IDs aren't used in the path, so just pass the same ID.
+ final FileOutputStreamWithPath out =
+ mService.openIconFileForWrite(userId, makePackageShortcut(packageName, "id"));
+ out.close();
+ return out.getFile();
+ }
+
+ public void testOpenIconFileForWrite() throws IOException {
+ mInjectedCurrentTimeMillis = 1000;
+
+ final File p10_1_1 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_1);
+ final File p10_1_2 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_1);
+
+ final File p10_2_1 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_2);
+ final File p10_2_2 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_2);
+
+ final File p11_1_1 = openIconFileForWriteAndGetPath(11, CALLING_PACKAGE_1);
+ final File p11_1_2 = openIconFileForWriteAndGetPath(11, CALLING_PACKAGE_1);
+
+ mInjectedCurrentTimeMillis++;
+
+ final File p10_1_3 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_1);
+ final File p10_1_4 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_1);
+ final File p10_1_5 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_1);
+
+ final File p10_2_3 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_2);
+ final File p11_1_3 = openIconFileForWriteAndGetPath(11, CALLING_PACKAGE_1);
+
+ // Make sure their paths are all unique
+ assertAllUnique(list(
+ p10_1_1,
+ p10_1_2,
+ p10_1_3,
+ p10_1_4,
+ p10_1_5,
+
+ p10_2_1,
+ p10_2_2,
+ p10_2_3,
+
+ p11_1_1,
+ p11_1_2,
+ p11_1_3
+ ));
+
+ // Check each set has the same parent.
+ assertEquals(p10_1_1.getParent(), p10_1_2.getParent());
+ assertEquals(p10_1_1.getParent(), p10_1_3.getParent());
+ assertEquals(p10_1_1.getParent(), p10_1_4.getParent());
+ assertEquals(p10_1_1.getParent(), p10_1_5.getParent());
+
+ assertEquals(p10_2_1.getParent(), p10_2_2.getParent());
+ assertEquals(p10_2_1.getParent(), p10_2_3.getParent());
+
+ assertEquals(p11_1_1.getParent(), p11_1_2.getParent());
+ assertEquals(p11_1_1.getParent(), p11_1_3.getParent());
+
+ // Check the parents are still unique.
+ assertAllUnique(list(
+ p10_1_1.getParent(),
+ p10_2_1.getParent(),
+ p11_1_1.getParent()
+ ));
+
+ // All files created at the same time for the same package/user, expcet for the first ones,
+ // will have "_" in the path.
+ assertFalse(p10_1_1.getName().contains("_"));
+ assertTrue(p10_1_2.getName().contains("_"));
+ assertFalse(p10_1_3.getName().contains("_"));
+ assertTrue(p10_1_4.getName().contains("_"));
+ assertTrue(p10_1_5.getName().contains("_"));
+
+ assertFalse(p10_2_1.getName().contains("_"));
+ assertTrue(p10_2_2.getName().contains("_"));
+ assertFalse(p10_2_3.getName().contains("_"));
+
+ assertFalse(p11_1_1.getName().contains("_"));
+ assertTrue(p11_1_2.getName().contains("_"));
+ assertFalse(p11_1_3.getName().contains("_"));
+ }
+
+ public void testUpdateShortcuts() {
+ runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"),
+ makeShortcut("s2"),
+ makeShortcut("s3"),
+ makeShortcut("s4"),
+ makeShortcut("s5")
+ )));
+ });
+ runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"),
+ makeShortcut("s2"),
+ makeShortcut("s3"),
+ makeShortcut("s4"),
+ makeShortcut("s5")
+ )));
+ });
+ runWithCaller(LAUNCHER_1, UserHandle.USER_SYSTEM, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2", "s3"),
+ getCallingUser());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s4", "s5"),
+ getCallingUser());
+ });
+ runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
+ mManager.removeDynamicShortcuts(list("s1"));
+ mManager.removeDynamicShortcuts(list("s2"));
+ });
+ runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
+ mManager.removeDynamicShortcuts(list("s1"));
+ mManager.removeDynamicShortcuts(list("s3"));
+ mManager.removeDynamicShortcuts(list("s5"));
+ });
+ runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
+ assertShortcutIds(assertAllDynamic(
+ mManager.getDynamicShortcuts()),
+ "s3", "s4", "s5");
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s2", "s3");
+ });
+ runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
+ assertShortcutIds(assertAllDynamic(
+ mManager.getDynamicShortcuts()),
+ "s2", "s4");
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s4", "s5");
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
+ ShortcutInfo s2 = makeShortcutBuilder()
+ .setId("s2")
+ .setIcon(Icon.createWithResource(getTestContext(), R.drawable.black_32x32))
+ .build();
+
+ ShortcutInfo s4 = makeShortcutBuilder()
+ .setId("s4")
+ .setTitle("new title")
+ .build();
+
+ mManager.updateShortcuts(list(s2, s4));
+ });
+ runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
+ ShortcutInfo s2 = makeShortcutBuilder()
+ .setId("s2")
+ .setIntent(makeIntent(Intent.ACTION_ANSWER, ShortcutActivity.class,
+ "key1", "val1"))
+ .build();
+
+ ShortcutInfo s4 = makeShortcutBuilder()
+ .setId("s4")
+ .setIntent(new Intent(Intent.ACTION_ALL_APPS))
+ .build();
+
+ mManager.updateShortcuts(list(s2, s4));
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
+ assertShortcutIds(assertAllDynamic(
+ mManager.getDynamicShortcuts()),
+ "s3", "s4", "s5");
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s2", "s3");
+
+ ShortcutInfo s = getCallerShortcut("s2");
+ assertTrue(s.hasIconResource());
+ assertEquals(R.drawable.black_32x32, s.getIconResourceId());
+ assertEquals("string/r" + R.drawable.black_32x32, s.getIconResName());
+ assertEquals("Title-s2", s.getTitle());
+
+ s = getCallerShortcut("s4");
+ assertFalse(s.hasIconResource());
+ assertEquals(0, s.getIconResourceId());
+ assertEquals("new title", s.getTitle());
+ });
+ runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
+ assertShortcutIds(assertAllDynamic(
+ mManager.getDynamicShortcuts()),
+ "s2", "s4");
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s4", "s5");
+
+ ShortcutInfo s = getCallerShortcut("s2");
+ assertFalse(s.hasIconResource());
+ assertEquals(0, s.getIconResourceId());
+ assertEquals("Title-s2", s.getTitle());
+ assertEquals(Intent.ACTION_ANSWER, s.getIntent().getAction());
+ assertEquals(1, s.getIntent().getExtras().size());
+
+ s = getCallerShortcut("s4");
+ assertFalse(s.hasIconResource());
+ assertEquals(0, s.getIconResourceId());
+ assertEquals("Title-s4", s.getTitle());
+ assertEquals(Intent.ACTION_ALL_APPS, s.getIntent().getAction());
+ assertBundleEmpty(s.getIntent().getExtras());
+ });
+ // TODO Check with other fields too.
+
+ // TODO Check bitmap removal too.
+
+ mRunningUsers.put(USER_11, true);
+
+ runWithCaller(CALLING_PACKAGE_2, USER_11, () -> {
+ mManager.updateShortcuts(list());
+ });
+ }
+
+ public void testUpdateShortcuts_icons() {
+ runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1")
+ )));
+
+ // Set resource icon
+ assertTrue(mManager.updateShortcuts(list(
+ new ShortcutInfo.Builder(mClientContext, "s1")
+ .setIcon(Icon.createWithResource(getTestContext(), R.drawable.black_32x32))
+ .build()
+ )));
+
+ assertWith(getCallerShortcuts())
+ .forShortcutWithId("s1", si -> {
+ assertTrue(si.hasIconResource());
+ assertEquals(R.drawable.black_32x32, si.getIconResourceId());
+ });
+
+ // Set bitmap icon
+ assertTrue(mManager.updateShortcuts(list(
+ new ShortcutInfo.Builder(mClientContext, "s1")
+ .setIcon(Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.black_64x64)))
+ .build()
+ )));
+
+ assertWith(getCallerShortcuts())
+ .forShortcutWithId("s1", si -> {
+ assertTrue(si.hasIconFile());
+ });
+
+ mInjectedCurrentTimeMillis += INTERVAL; // reset throttling
+
+ // Do it again, with the reverse order (bitmap -> icon)
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1")
+ )));
+
+ // Set bitmap icon
+ assertTrue(mManager.updateShortcuts(list(
+ new ShortcutInfo.Builder(mClientContext, "s1")
+ .setIcon(Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.black_64x64)))
+ .build()
+ )));
+
+ assertWith(getCallerShortcuts())
+ .forShortcutWithId("s1", si -> {
+ assertTrue(si.hasIconFile());
+ });
+
+ // Set resource icon
+ assertTrue(mManager.updateShortcuts(list(
+ new ShortcutInfo.Builder(mClientContext, "s1")
+ .setIcon(Icon.createWithResource(getTestContext(), R.drawable.black_32x32))
+ .build()
+ )));
+
+ assertWith(getCallerShortcuts())
+ .forShortcutWithId("s1", si -> {
+ assertTrue(si.hasIconResource());
+ assertEquals(R.drawable.black_32x32, si.getIconResourceId());
+ });
+ });
+ }
+
+ // === Test for launcher side APIs ===
+
+ public void testGetShortcuts() {
+
+ // Set up shortcuts.
+
+ setCaller(CALLING_PACKAGE_1);
+ final ShortcutInfo s1_1 = makeShortcut("s1");
+ final ShortcutInfo s1_2 = makeShortcut("s2");
+
+ assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2)));
+
+ // Because setDynamicShortcuts will update the timestamps when ranks are changing,
+ // we explicitly set timestamps here.
+ getCallerShortcut("s1").setTimestamp(5000);
+ getCallerShortcut("s2").setTimestamp(1000);
+
+ setCaller(CALLING_PACKAGE_2);
+ final ShortcutInfo s2_2 = makeShortcut("s2");
+ final ShortcutInfo s2_3 = makeShortcutWithActivity("s3",
+ makeComponent(ShortcutActivity2.class));
+ final ShortcutInfo s2_4 = makeShortcutWithActivity("s4",
+ makeComponent(ShortcutActivity.class));
+ assertTrue(mManager.setDynamicShortcuts(list(s2_2, s2_3, s2_4)));
+
+ getCallerShortcut("s2").setTimestamp(1500);
+ getCallerShortcut("s3").setTimestamp(3000);
+ getCallerShortcut("s4").setTimestamp(500);
+
+ setCaller(CALLING_PACKAGE_3);
+ final ShortcutInfo s3_2 = makeShortcut("s3");
+ assertTrue(mManager.setDynamicShortcuts(list(s3_2)));
+
+ getCallerShortcut("s3").setTimestamp(START_TIME + 5000);
+
+ setCaller(LAUNCHER_1);
+
+ // Get dynamic
+ assertAllDynamic(assertAllHaveTitle(assertAllNotHaveIntents(assertAllStringsResolved(
+ assertShortcutIds(
+ assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
+ "s1", "s2")))));
+
+ // Get pinned
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_PINNED), getCallingUser())
+ /* none */);
+
+ // Get both, with timestamp
+ assertAllDynamic(assertAllHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+ assertAllNotKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 1000, CALLING_PACKAGE_2,
+ /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_PINNED | ShortcutQuery.FLAG_GET_DYNAMIC),
+ getCallingUser())),
+ "s2", "s3"))));
+
+ // FLAG_GET_KEY_FIELDS_ONLY
+ assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+ assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 1000, CALLING_PACKAGE_2,
+ /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+ getCallingUser())),
+ "s2", "s3"))));
+
+ // Filter by activity
+ assertAllDynamic(assertAllHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+ assertAllNotKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 0, CALLING_PACKAGE_2,
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()),
+ ShortcutQuery.FLAG_GET_PINNED | ShortcutQuery.FLAG_GET_DYNAMIC),
+ getCallingUser())),
+ "s4"))));
+
+ // With ID.
+ assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+ assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 1000, CALLING_PACKAGE_2, list("s3"),
+ /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+ getCallingUser())),
+ "s3"))));
+ assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+ assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 1000, CALLING_PACKAGE_2, list("s3", "s2", "ss"),
+ /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+ getCallingUser())),
+ "s2", "s3"))));
+ assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+ assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 1000, CALLING_PACKAGE_2, list("s3x", "s2x"),
+ /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+ getCallingUser()))
+ /* empty */))));
+ assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+ assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 1000, CALLING_PACKAGE_2, list(),
+ /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+ getCallingUser()))
+ /* empty */))));
+
+ // Pin some shortcuts.
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s3", "s4"), getCallingUser());
+
+ // Pinned ones only
+ assertAllPinned(assertAllHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+ assertAllNotKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 1000, CALLING_PACKAGE_2,
+ /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_PINNED),
+ getCallingUser())),
+ "s3"))));
+
+ // All packages.
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 5000, /* package= */ null,
+ /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED),
+ getCallingUser())),
+ "s1", "s3");
+
+ assertExpectException(
+ IllegalArgumentException.class, "package name must also be set", () -> {
+ mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 0, /* package= */ null, list("id"),
+ /* activity =*/ null, /* flags */ 0), getCallingUser());
+ });
+
+ // TODO More tests: pinned but dynamic.
+ }
+
+ public void testGetShortcuts_shortcutKinds() throws Exception {
+ // Create 3 manifest and 3 dynamic shortcuts
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_3);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+
+ // Pin 2 and 3
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms2", "ms3", "s2", "s3"),
+ HANDLE_USER_0);
+ });
+
+ // Remove ms3 and s3
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"))));
+ });
+
+ // Check their status.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1", "ms2", "ms3", "s1", "s2", "s3")
+
+ .selectByIds("ms1", "ms2")
+ .areAllManifest()
+ .areAllImmutable()
+ .areAllNotDynamic()
+
+ .revertToOriginalList()
+ .selectByIds("ms3")
+ .areAllNotManifest()
+ .areAllImmutable()
+ .areAllDisabled()
+ .areAllNotDynamic()
+
+ .revertToOriginalList()
+ .selectByIds("s1", "s2")
+ .areAllNotManifest()
+ .areAllMutable()
+ .areAllDynamic()
+
+ .revertToOriginalList()
+ .selectByIds("s3")
+ .areAllNotManifest()
+ .areAllMutable()
+ .areAllEnabled()
+ .areAllNotDynamic()
+
+ .revertToOriginalList()
+ .selectByIds("s1", "ms1")
+ .areAllNotPinned()
+
+ .revertToOriginalList()
+ .selectByIds("s2", "s3", "ms2", "ms3")
+ .areAllPinned()
+ ;
+ });
+
+ // Finally, actual tests.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertWith(mLauncherApps.getShortcuts(
+ buildQueryWithFlags(ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0))
+ .haveIds("s1", "s2");
+ assertWith(mLauncherApps.getShortcuts(
+ buildQueryWithFlags(ShortcutQuery.FLAG_GET_MANIFEST), HANDLE_USER_0))
+ .haveIds("ms1", "ms2");
+ assertWith(mLauncherApps.getShortcuts(
+ buildQueryWithFlags(ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0))
+ .haveIds("s2", "s3", "ms2", "ms3");
+
+ assertWith(mLauncherApps.getShortcuts(
+ buildQueryWithFlags(
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED
+ ), HANDLE_USER_0))
+ .haveIds("s1", "s2", "s3", "ms2", "ms3");
+
+ assertWith(mLauncherApps.getShortcuts(
+ buildQueryWithFlags(
+ ShortcutQuery.FLAG_GET_MANIFEST | ShortcutQuery.FLAG_GET_PINNED
+ ), HANDLE_USER_0))
+ .haveIds("ms1", "s2", "s3", "ms2", "ms3");
+
+ assertWith(mLauncherApps.getShortcuts(
+ buildQueryWithFlags(
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_MANIFEST
+ ), HANDLE_USER_0))
+ .haveIds("ms1", "ms2", "s1", "s2");
+
+ assertWith(mLauncherApps.getShortcuts(
+ buildQueryWithFlags(
+ ShortcutQuery.FLAG_GET_ALL_KINDS
+ ), HANDLE_USER_0))
+ .haveIds("ms1", "ms2", "ms3", "s1", "s2", "s3");
+ });
+ }
+
+ public void testGetShortcuts_resolveStrings() throws Exception {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ ShortcutInfo si = new ShortcutInfo.Builder(mClientContext)
+ .setId("id")
+ .setActivity(new ComponentName(mClientContext, "dummy"))
+ .setTitleResId(10)
+ .setTextResId(11)
+ .setDisabledMessageResId(12)
+ .setIntent(makeIntent("action", ShortcutActivity.class))
+ .build();
+ mManager.setDynamicShortcuts(list(si));
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ ShortcutInfo si = new ShortcutInfo.Builder(mClientContext)
+ .setId("id")
+ .setActivity(new ComponentName(mClientContext, "dummy"))
+ .setTitleResId(10)
+ .setTextResId(11)
+ .setDisabledMessageResId(12)
+ .setIntent(makeIntent("action", ShortcutActivity.class))
+ .build();
+ mManager.setDynamicShortcuts(list(si));
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ final ShortcutQuery q = new ShortcutQuery();
+ q.setQueryFlags(ShortcutQuery.FLAG_GET_DYNAMIC);
+
+ // USER 0
+ List<ShortcutInfo> ret = assertShortcutIds(
+ assertAllStringsResolved(mLauncherApps.getShortcuts(q, HANDLE_USER_0)),
+ "id");
+ assertEquals("string-com.android.test.1-user:0-res:10/en", ret.get(0).getTitle());
+ assertEquals("string-com.android.test.1-user:0-res:11/en", ret.get(0).getText());
+ assertEquals("string-com.android.test.1-user:0-res:12/en",
+ ret.get(0).getDisabledMessage());
+
+ // USER P0
+ ret = assertShortcutIds(
+ assertAllStringsResolved(mLauncherApps.getShortcuts(q, HANDLE_USER_P0)),
+ "id");
+ assertEquals("string-com.android.test.1-user:20-res:10/en", ret.get(0).getTitle());
+ assertEquals("string-com.android.test.1-user:20-res:11/en", ret.get(0).getText());
+ assertEquals("string-com.android.test.1-user:20-res:12/en",
+ ret.get(0).getDisabledMessage());
+ });
+ }
+
+ // TODO resource
+ public void testGetShortcutInfo() {
+ // Create shortcuts.
+ setCaller(CALLING_PACKAGE_1);
+ final ShortcutInfo s1_1 = makeShortcut(
+ "s1",
+ "Title 1",
+ makeComponent(ShortcutActivity.class),
+ /* icon =*/ null,
+ makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
+ "key1", "val1", "nest", makeBundle("key", 123)),
+ /* weight */ 10);
+
+ final ShortcutInfo s1_2 = makeShortcut(
+ "s2",
+ "Title 2",
+ /* activity */ null,
+ /* icon =*/ null,
+ makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
+ /* weight */ 12);
+
+ assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2)));
+ dumpsysOnLogcat();
+
+ setCaller(CALLING_PACKAGE_2);
+ final ShortcutInfo s2_1 = makeShortcut(
+ "s1",
+ "ABC",
+ makeComponent(ShortcutActivity2.class),
+ /* icon =*/ null,
+ makeIntent(Intent.ACTION_ANSWER, ShortcutActivity2.class,
+ "key1", "val1", "nest", makeBundle("key", 123)),
+ /* weight */ 10);
+ assertTrue(mManager.setDynamicShortcuts(list(s2_1)));
+ dumpsysOnLogcat();
+
+ // Pin some.
+ setCaller(LAUNCHER_1);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s2"), getCallingUser());
+
+ dumpsysOnLogcat();
+
+ // Delete some.
+ setCaller(CALLING_PACKAGE_1);
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+ mManager.removeDynamicShortcuts(list("s2"));
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+
+ dumpsysOnLogcat();
+
+ setCaller(LAUNCHER_1);
+ List<ShortcutInfo> list;
+
+ // Note we don't guarantee the orders.
+ list = assertShortcutIds(assertAllHaveTitle(assertAllNotHaveIntents(
+ assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcutInfo(CALLING_PACKAGE_1,
+ list("s2", "s1", "s3", null), getCallingUser())))),
+ "s1", "s2");
+ assertEquals("Title 1", findById(list, "s1").getTitle());
+ assertEquals("Title 2", findById(list, "s2").getTitle());
+
+ assertShortcutIds(assertAllHaveTitle(assertAllNotHaveIntents(
+ mLauncherApps.getShortcutInfo(CALLING_PACKAGE_1,
+ list("s3"), getCallingUser())))
+ /* none */);
+
+ list = assertShortcutIds(assertAllHaveTitle(assertAllNotHaveIntents(
+ mLauncherApps.getShortcutInfo(CALLING_PACKAGE_2,
+ list("s1", "s2", "s3"), getCallingUser()))),
+ "s1");
+ assertEquals("ABC", findById(list, "s1").getTitle());
+ }
+
+ public void testPinShortcutAndGetPinnedShortcuts() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
+ final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
+
+ assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2)));
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ final ShortcutInfo s2_2 = makeShortcutWithTimestamp("s2", 1500);
+ final ShortcutInfo s2_3 = makeShortcutWithTimestamp("s3", 3000);
+ final ShortcutInfo s2_4 = makeShortcutWithTimestamp("s4", 500);
+ assertTrue(mManager.setDynamicShortcuts(list(s2_2, s2_3, s2_4)));
+ });
+
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ final ShortcutInfo s3_2 = makeShortcutWithTimestamp("s2", 1000);
+ assertTrue(mManager.setDynamicShortcuts(list(s3_2)));
+ });
+
+ // Pin some.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s2", "s3"), getCallingUser());
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s3", "s4", "s5"), getCallingUser());
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_3,
+ list("s3"), getCallingUser()); // Note ID doesn't exist
+ });
+
+ // Delete some.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+ mManager.removeDynamicShortcuts(list("s2"));
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+
+ assertShortcutIds(mManager.getDynamicShortcuts(), "s1");
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
+ mManager.removeDynamicShortcuts(list("s3"));
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
+
+ assertShortcutIds(mManager.getDynamicShortcuts(), "s2", "s4");
+ });
+
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
+ mManager.removeDynamicShortcuts(list("s2"));
+ assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
+
+ assertEmpty(mManager.getDynamicShortcuts());
+ });
+
+ // Get pinned shortcuts from launcher
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ // CALLING_PACKAGE_1 deleted s2, but it's pinned, so it still exists.
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(assertAllEnabled(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser())))),
+ "s2");
+
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(assertAllEnabled(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser())))),
+ "s3", "s4");
+
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(assertAllEnabled(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_3,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))))
+ /* none */);
+ });
+ }
+
+ /**
+ * This is similar to the above test, except it used "disable" instead of "remove". It also
+ * does "enable".
+ */
+ public void testDisableAndEnableShortcuts() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
+ final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
+
+ assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2)));
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ final ShortcutInfo s2_2 = makeShortcutWithTimestamp("s2", 1500);
+ final ShortcutInfo s2_3 = makeShortcutWithTimestamp("s3", 3000);
+ final ShortcutInfo s2_4 = makeShortcutWithTimestamp("s4", 500);
+ assertTrue(mManager.setDynamicShortcuts(list(s2_2, s2_3, s2_4)));
+ });
+
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ final ShortcutInfo s3_2 = makeShortcutWithTimestamp("s2", 1000);
+ assertTrue(mManager.setDynamicShortcuts(list(s3_2)));
+ });
+
+ // Pin some.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s2", "s3"), getCallingUser());
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s3", "s4", "s5"), getCallingUser());
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_3,
+ list("s3"), getCallingUser()); // Note ID doesn't exist
+ });
+
+ // Disable some.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+
+ mManager.disableShortcuts(list("s2"));
+
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+ assertShortcutIds(mManager.getDynamicShortcuts(), "s1");
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
+
+ // disable should work even if a shortcut is not dynamic, so try calling "remove" first
+ // here.
+ mManager.removeDynamicShortcuts(list("s3"));
+ mManager.disableShortcuts(list("s3"));
+
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
+ assertShortcutIds(mManager.getDynamicShortcuts(), "s2", "s4");
+ });
+
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
+
+ mManager.disableShortcuts(list("s2"));
+
+ assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
+
+ assertEmpty(mManager.getDynamicShortcuts());
+ assertEmpty(getCallerShortcuts());
+ });
+
+ // Get pinned shortcuts from launcher
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ // CALLING_PACKAGE_1 deleted s2, but it's pinned, so it still exists, and disabled.
+ assertWith(mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))
+ .haveIds("s2")
+ .areAllPinned()
+ .areAllNotWithKeyFieldsOnly()
+ .areAllDisabled();
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_0,
+ ActivityNotFoundException.class);
+
+ // Here, s4 is still enabled and launchable, but s3 is disabled.
+ assertWith(mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))
+ .haveIds("s3", "s4")
+ .areAllPinned()
+ .areAllNotWithKeyFieldsOnly()
+
+ .selectByIds("s3")
+ .areAllDisabled()
+
+ .revertToOriginalList()
+ .selectByIds("s4")
+ .areAllEnabled();
+
+ assertStartShortcutThrowsException(CALLING_PACKAGE_2, "s3", USER_0,
+ ActivityNotFoundException.class);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s4", USER_0);
+
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(assertAllEnabled(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_3,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))))
+ /* none */);
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.enableShortcuts(list("s2"));
+
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+ assertShortcutIds(mManager.getDynamicShortcuts(), "s1");
+ });
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ // CALLING_PACKAGE_1 deleted s2, but it's pinned, so it still exists.
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(assertAllEnabled(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser())))),
+ "s2");
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ });
+ }
+
+ public void testDisableShortcuts_thenRepublish() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(
+ CALLING_PACKAGE_1, list("s1", "s2", "s3"), HANDLE_USER_0);
+ });
+
+ mManager.disableShortcuts(list("s1", "s2", "s3"));
+
+ assertWith(getCallerShortcuts())
+ .haveIds("s1", "s2", "s3")
+ .areAllNotDynamic()
+ .areAllPinned()
+ .areAllDisabled();
+
+ // Make sure updateShortcuts() will not re-enable them.
+ assertTrue(mManager.updateShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+
+ assertWith(getCallerShortcuts())
+ .haveIds("s1", "s2", "s3")
+ .areAllNotDynamic()
+ .areAllPinned()
+ .areAllDisabled();
+
+ // Re-publish s1 with setDynamicShortcuts.
+ mInjectedCurrentTimeMillis += INTERVAL; // reset throttling
+
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"))));
+
+ assertWith(getCallerShortcuts())
+ .haveIds("s1", "s2", "s3")
+
+ .selectByIds("s1")
+ .areAllDynamic()
+ .areAllPinned()
+ .areAllEnabled()
+
+ .revertToOriginalList()
+ .selectByIds("s2", "s3")
+ .areAllNotDynamic()
+ .areAllPinned()
+ .areAllDisabled();
+
+ // Re-publish s2 with addDynamicShortcuts.
+ mInjectedCurrentTimeMillis += INTERVAL; // reset throttling
+
+ assertTrue(mManager.addDynamicShortcuts(list(
+ makeShortcut("s2"))));
+
+ assertWith(getCallerShortcuts())
+ .haveIds("s1", "s2", "s3")
+
+ .selectByIds("s1", "s2")
+ .areAllDynamic()
+ .areAllPinned()
+ .areAllEnabled()
+
+ .revertToOriginalList()
+ .selectByIds("s3")
+ .areAllNotDynamic()
+ .areAllPinned()
+ .areAllDisabled();
+ });
+ }
+
+ public void testPinShortcutAndGetPinnedShortcuts_multi() {
+ // Create some shortcuts.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+
+ dumpsysOnLogcat();
+
+ // Pin some.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s3", "s4"), getCallingUser());
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s1", "s2", "s4"), getCallingUser());
+ });
+
+ dumpsysOnLogcat();
+
+ // Delete some.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s3");
+ mManager.removeDynamicShortcuts(list("s3"));
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s3");
+ });
+
+ dumpsysOnLogcat();
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s1", "s2");
+ mManager.removeDynamicShortcuts(list("s1"));
+ mManager.removeDynamicShortcuts(list("s3"));
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s1", "s2");
+ });
+
+ dumpsysOnLogcat();
+
+ // Get pinned shortcuts from launcher
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s3");
+
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s1", "s2");
+
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
+ | ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
+ "s1", "s2", "s3");
+
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
+ | ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
+ "s1", "s2");
+ });
+
+ dumpsysOnLogcat();
+
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ // Launcher2 still has no pinned ones.
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser())))
+ /* none */);
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser())))
+ /* none */);
+
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
+ | ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
+ "s1", "s2");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
+ | ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
+ "s2");
+
+ // Now pin some.
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s1", "s2"), getCallingUser());
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s1", "s2"), getCallingUser());
+
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
+ | ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
+ "s1", "s2");
+
+ // S1 was not visible to it, so shouldn't be pinned.
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
+ | ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
+ "s2");
+ });
+
+ // Re-initialize and load from the files.
+ mService.saveDirtyInfo();
+ initService();
+
+ // Load from file.
+ mService.handleUnlockUser(USER_0);
+
+ // Make sure package info is restored too.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s3");
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s1", "s2");
+ });
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
+ | ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
+ "s1", "s2");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
+ | ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
+ "s2");
+ });
+
+ // Delete all dynamic.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.removeAllDynamicShortcuts();
+
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s1", "s2", "s3");
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ mManager.removeAllDynamicShortcuts();
+
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s2", "s1");
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s3");
+
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s1", "s2");
+
+ // from all packages.
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, null,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s1", "s2", "s3");
+
+ // Update pined. Note s2 and s3 are actually available, but not visible to this
+ // launcher, so still can't be pinned.
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s3", "s4"),
+ getCallingUser());
+
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s3");
+ });
+ // Re-publish s1.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
+
+ assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()), "s1");
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s1", "s2", "s3");
+ });
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s3");
+
+ // Now "s1" is visible, so can be pinned.
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s3", "s4"),
+ getCallingUser());
+
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s1", "s3");
+ });
+
+ // Now clear pinned shortcuts. First, from launcher 1.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list(), getCallingUser());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list(), getCallingUser());
+
+ assertEquals(0,
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()).size());
+ assertEquals(0,
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()).size());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()), "s1");
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s1", "s2");
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s2");
+ });
+
+ // Clear all pins from launcher 2.
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list(), getCallingUser());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list(), getCallingUser());
+
+ assertEquals(0,
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()).size());
+ assertEquals(0,
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()).size());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()), "s1");
+ assertEquals(0, mManager.getPinnedShortcuts().size());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertEquals(0, mManager.getPinnedShortcuts().size());
+ });
+ }
+
+ public void testPinShortcutAndGetPinnedShortcuts_crossProfile_plusLaunch() {
+ // Create some shortcuts.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+
+ mRunningUsers.put(USER_10, true);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"),
+ makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6"))));
+ });
+
+ // Pin some shortcuts and see the result.
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s1"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s1", "s2", "s3"), HANDLE_USER_0);
+ });
+
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s2"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s2", "s3"), HANDLE_USER_0);
+ });
+
+ runWithCaller(LAUNCHER_2, USER_P0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s3"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s3"), HANDLE_USER_0);
+ });
+
+ runWithCaller(LAUNCHER_2, USER_10, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s1", "s2", "s3"), HANDLE_USER_10);
+ });
+
+ // Cross profile pinning.
+ final int PIN_AND_DYNAMIC = ShortcutQuery.FLAG_GET_PINNED | ShortcutQuery.FLAG_GET_DYNAMIC;
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s4", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s5", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s6", USER_10,
+ SecurityException.class);
+ });
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s2");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s4", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s5", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s6", USER_10,
+ SecurityException.class);
+ });
+ runWithCaller(LAUNCHER_2, USER_P0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s4", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s5", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s6", USER_10,
+ SecurityException.class);
+ });
+ runWithCaller(LAUNCHER_2, USER_10, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_10)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_10)),
+ "s1", "s2", "s3", "s4", "s5", "s6");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_10)),
+ "s1", "s2", "s3", "s4", "s5", "s6");
+ });
+
+ // Remove some dynamic shortcuts.
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"))));
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"))));
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"))));
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_0,
+ ActivityNotFoundException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_0,
+ ActivityNotFoundException.class);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s4", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s5", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s6", USER_10,
+ SecurityException.class);
+ });
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s2");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_0,
+ ActivityNotFoundException.class);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s4", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s5", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s6", USER_10,
+ SecurityException.class);
+ });
+ runWithCaller(LAUNCHER_2, USER_P0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s3");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_0,
+ ActivityNotFoundException.class);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_2, "s2", USER_0,
+ ActivityNotFoundException.class);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s4", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s5", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s6", USER_10,
+ SecurityException.class);
+ });
+ runWithCaller(LAUNCHER_2, USER_10, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_10)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_10)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_10)),
+ "s1", "s2", "s3");
+
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_0,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_0,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_0,
+ SecurityException.class);
+
+ assertStartShortcutThrowsException(CALLING_PACKAGE_2, "s1", USER_0,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_2, "s2", USER_0,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_2, "s3", USER_0,
+ SecurityException.class);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s4", USER_10,
+ ActivityNotFoundException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s5", USER_10,
+ ActivityNotFoundException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s6", USER_10,
+ ActivityNotFoundException.class);
+ });
+
+ // Save & load and make sure we still have the same information.
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_0);
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_0,
+ ActivityNotFoundException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_0,
+ ActivityNotFoundException.class);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s4", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s5", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s6", USER_10,
+ SecurityException.class);
+ });
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s2");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_0,
+ ActivityNotFoundException.class);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s4", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s5", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s6", USER_10,
+ SecurityException.class);
+ });
+ runWithCaller(LAUNCHER_2, USER_P0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s3");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_0,
+ ActivityNotFoundException.class);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_2, "s2", USER_0,
+ ActivityNotFoundException.class);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s4", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s5", USER_10,
+ SecurityException.class);
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s6", USER_10,
+ SecurityException.class);
+ });
+ }
+
+ public void testStartShortcut() {
+ // Create some shortcuts.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ final ShortcutInfo s1_1 = makeShortcut(
+ "s1",
+ "Title 1",
+ makeComponent(ShortcutActivity.class),
+ /* icon =*/ null,
+ new Intent[] {makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
+ "key1", "val1", "nest", makeBundle("key", 123))
+ .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK),
+ new Intent("act2").setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)},
+ /* rank */ 10);
+
+ final ShortcutInfo s1_2 = makeShortcut(
+ "s2",
+ "Title 2",
+ /* activity */ null,
+ /* icon =*/ null,
+ makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
+ /* rank */ 12);
+
+ final ShortcutInfo s1_3 = makeShortcut("s3");
+
+ assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2, s1_3)));
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ final ShortcutInfo s2_1 = makeShortcut(
+ "s1",
+ "ABC",
+ makeComponent(ShortcutActivity.class),
+ /* icon =*/ null,
+ makeIntent(Intent.ACTION_ANSWER, ShortcutActivity.class,
+ "key1", "val1", "nest", makeBundle("key", 123)),
+ /* weight */ 10);
+ assertTrue(mManager.setDynamicShortcuts(list(s2_1)));
+ });
+
+ // Pin some.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s1", "s2"), getCallingUser());
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s1"), getCallingUser());
+ });
+
+ // Just to make it complicated, delete some.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.removeDynamicShortcuts(list("s2"));
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ final Intent[] intents = launchShortcutAndGetIntents(CALLING_PACKAGE_1, "s1", USER_0);
+ assertEquals(ShortcutActivity2.class.getName(),
+ intents[0].getComponent().getClassName());
+ assertEquals(Intent.ACTION_ASSIST,
+ intents[0].getAction());
+ assertEquals(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK,
+ intents[0].getFlags());
+
+ assertEquals("act2",
+ intents[1].getAction());
+ assertEquals(Intent.FLAG_ACTIVITY_NO_ANIMATION,
+ intents[1].getFlags());
+
+ assertEquals(
+ ShortcutActivity3.class.getName(),
+ launchShortcutAndGetIntent(CALLING_PACKAGE_1, "s2", USER_0)
+ .getComponent().getClassName());
+ assertEquals(
+ ShortcutActivity.class.getName(),
+ launchShortcutAndGetIntent(CALLING_PACKAGE_2, "s1", USER_0)
+ .getComponent().getClassName());
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutNotLaunched("no-such-package", "s2", USER_0);
+ assertShortcutNotLaunched(CALLING_PACKAGE_1, "xxxx", USER_0);
+ });
+
+ // LAUNCHER_1 is no longer the default launcher
+ setDefaultLauncherChecker((pkg, userId) -> false);
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ // Not the default launcher, but pinned shortcuts are still lauchable.
+ final Intent[] intents = launchShortcutAndGetIntents(CALLING_PACKAGE_1, "s1", USER_0);
+ assertEquals(ShortcutActivity2.class.getName(),
+ intents[0].getComponent().getClassName());
+ assertEquals(Intent.ACTION_ASSIST,
+ intents[0].getAction());
+ assertEquals(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK,
+ intents[0].getFlags());
+
+ assertEquals("act2",
+ intents[1].getAction());
+ assertEquals(Intent.FLAG_ACTIVITY_NO_ANIMATION,
+ intents[1].getFlags());
+ assertEquals(
+ ShortcutActivity3.class.getName(),
+ launchShortcutAndGetIntent(CALLING_PACKAGE_1, "s2", USER_0)
+ .getComponent().getClassName());
+ assertEquals(
+ ShortcutActivity.class.getName(),
+ launchShortcutAndGetIntent(CALLING_PACKAGE_2, "s1", USER_0)
+ .getComponent().getClassName());
+
+ // Not pinned, so not lauchable.
+ });
+
+ // Test inner errors.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ // Not launchable.
+ doReturn(ActivityManager.START_CLASS_NOT_FOUND)
+ .when(mMockActivityManagerInternal).startActivitiesAsPackage(
+ anyString(), anyInt(), any(Intent[].class), any(Bundle.class));
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_0,
+ ActivityNotFoundException.class);
+
+ // Still not launchable.
+ doReturn(ActivityManager.START_CLASS_NOT_FOUND)
+ .when(mMockActivityManagerInternal)
+ .startActivitiesAsPackage(
+ anyString(), anyInt(), any(Intent[].class), any(Bundle.class));
+ assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_0,
+ ActivityNotFoundException.class);
+ });
+
+
+ // TODO Check extra, etc
+ }
+
+ public void testLauncherCallback() throws Throwable {
+ // Disable throttling for this test.
+ mService.updateConfigurationLocked(
+ ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL + "=99999999,"
+ + ConfigConstants.KEY_MAX_SHORTCUTS + "=99999999"
+ );
+
+ setCaller(LAUNCHER_1, USER_0);
+
+ assertForLauncherCallback(mLauncherApps, () -> {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ }).assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_0)
+ .haveIds("s1", "s2", "s3")
+ .areAllWithKeyFieldsOnly()
+ .areAllDynamic();
+
+ // From different package.
+ assertForLauncherCallback(mLauncherApps, () -> {
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ }).assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_2, HANDLE_USER_0)
+ .haveIds("s1", "s2", "s3")
+ .areAllWithKeyFieldsOnly()
+ .areAllDynamic();
+
+ mRunningUsers.put(USER_10, true);
+
+ // Different user, callback shouldn't be called.
+ assertForLauncherCallback(mLauncherApps, () -> {
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ }).assertNoCallbackCalled();
+
+
+ // Test for addDynamicShortcuts.
+ assertForLauncherCallback(mLauncherApps, () -> {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s4"))));
+ });
+ }).assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_0)
+ .haveIds("s1", "s2", "s3", "s4")
+ .areAllWithKeyFieldsOnly()
+ .areAllDynamic();
+
+ // Test for remove
+ assertForLauncherCallback(mLauncherApps, () -> {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.removeDynamicShortcuts(list("s1"));
+ });
+ }).assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_0)
+ .haveIds("s2", "s3", "s4")
+ .areAllWithKeyFieldsOnly()
+ .areAllDynamic();
+
+ // Test for update
+ assertForLauncherCallback(mLauncherApps, () -> {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.updateShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"))));
+ });
+ }).assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_0)
+ // All remaining shortcuts will be passed regardless of what's been updated.
+ .haveIds("s2", "s3", "s4")
+ .areAllWithKeyFieldsOnly()
+ .areAllDynamic();
+
+ // Test for deleteAll
+ assertForLauncherCallback(mLauncherApps, () -> {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.removeAllDynamicShortcuts();
+ });
+ }).assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_0)
+ .isEmpty();
+
+ // Update package1 with manifest shortcuts
+ assertForLauncherCallback(mLauncherApps, () -> {
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+ }).assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_0)
+ .areAllManifest()
+ .areAllWithKeyFieldsOnly()
+ .haveIds("ms1", "ms2");
+
+ // Make sure pinned shortcuts are passed too.
+ // 1. Add dynamic shortcuts.
+ runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"))));
+ });
+
+ // 2. Pin some.
+ runWithCaller(LAUNCHER_1, UserHandle.USER_SYSTEM, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms2", "s2"), HANDLE_USER_0);
+ });
+ runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1", "ms2", "s1", "s2")
+ .areAllEnabled()
+
+ .selectByIds("ms1", "ms2")
+ .areAllManifest()
+
+ .revertToOriginalList()
+ .selectByIds("s1", "s2")
+ .areAllDynamic()
+ ;
+ });
+
+ // 3 Update the app with no manifest shortcuts. (Pinned one will survive.)
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_0);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ assertForLauncherCallback(mLauncherApps, () -> {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.removeDynamicShortcuts(list("s2"));
+
+ assertWith(getCallerShortcuts())
+ .haveIds("ms2", "s1", "s2")
+
+ .selectByIds("ms2")
+ .areAllNotManifest()
+ .areAllPinned()
+ .areAllImmutable()
+ .areAllDisabled()
+
+ .revertToOriginalList()
+ .selectByIds("s1")
+ .areAllDynamic()
+ .areAllNotPinned()
+ .areAllEnabled()
+
+ .revertToOriginalList()
+ .selectByIds("s2")
+ .areAllNotDynamic()
+ .areAllPinned()
+ .areAllEnabled()
+ ;
+ });
+ }).assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_0)
+ .haveIds("ms2", "s1", "s2")
+ .areAllWithKeyFieldsOnly();
+
+ // Remove CALLING_PACKAGE_2
+ assertForLauncherCallback(mLauncherApps, () -> {
+ uninstallPackage(USER_0, CALLING_PACKAGE_2);
+ mService.cleanUpPackageLocked(CALLING_PACKAGE_2, USER_0, USER_0,
+ /* appStillExists = */ false);
+ }).assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_2, HANDLE_USER_0)
+ .isEmpty();
+ }
+
+ public void testLauncherCallback_crossProfile() throws Throwable {
+ prepareCrossProfileDataSet();
+
+ final Handler h = new Handler(Looper.getMainLooper());
+
+ final LauncherApps.Callback c0_1 = mock(LauncherApps.Callback.class);
+ final LauncherApps.Callback c0_2 = mock(LauncherApps.Callback.class);
+ final LauncherApps.Callback c0_3 = mock(LauncherApps.Callback.class);
+ final LauncherApps.Callback c0_4 = mock(LauncherApps.Callback.class);
+
+ final LauncherApps.Callback cP0_1 = mock(LauncherApps.Callback.class);
+ final LauncherApps.Callback c10_1 = mock(LauncherApps.Callback.class);
+ final LauncherApps.Callback c10_2 = mock(LauncherApps.Callback.class);
+ final LauncherApps.Callback c11_1 = mock(LauncherApps.Callback.class);
+
+ final List<LauncherApps.Callback> all =
+ list(c0_1, c0_2, c0_3, c0_4, cP0_1, c10_1, c11_1);
+
+ setDefaultLauncherChecker((pkg, userId) -> {
+ switch (userId) {
+ case USER_0:
+ return LAUNCHER_2.equals(pkg);
+ case USER_P0:
+ return LAUNCHER_1.equals(pkg);
+ case USER_10:
+ return LAUNCHER_1.equals(pkg);
+ case USER_11:
+ return LAUNCHER_1.equals(pkg);
+ default:
+ return false;
+ }
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> mLauncherApps.registerCallback(c0_1, h));
+ runWithCaller(LAUNCHER_2, USER_0, () -> mLauncherApps.registerCallback(c0_2, h));
+ runWithCaller(LAUNCHER_3, USER_0, () -> mLauncherApps.registerCallback(c0_3, h));
+ runWithCaller(LAUNCHER_4, USER_0, () -> mLauncherApps.registerCallback(c0_4, h));
+ runWithCaller(LAUNCHER_1, USER_P0, () -> mLauncherApps.registerCallback(cP0_1, h));
+ runWithCaller(LAUNCHER_1, USER_10, () -> mLauncherApps.registerCallback(c10_1, h));
+ runWithCaller(LAUNCHER_2, USER_10, () -> mLauncherApps.registerCallback(c10_2, h));
+ runWithCaller(LAUNCHER_1, USER_11, () -> mLauncherApps.registerCallback(c11_1, h));
+
+ // User 0.
+
+ resetAll(all);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.removeDynamicShortcuts(list());
+ });
+ waitOnMainThread();
+
+ assertCallbackNotReceived(c0_1);
+ assertCallbackNotReceived(c0_3);
+ assertCallbackNotReceived(c0_4);
+ assertCallbackNotReceived(c10_1);
+ assertCallbackNotReceived(c10_2);
+ assertCallbackNotReceived(c11_1);
+ assertCallbackReceived(c0_2, HANDLE_USER_0, CALLING_PACKAGE_1, "s1", "s2", "s3");
+ assertCallbackReceived(cP0_1, HANDLE_USER_0, CALLING_PACKAGE_1, "s1", "s2", "s3", "s4");
+
+ // User 0, different package.
+
+ resetAll(all);
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ mManager.removeDynamicShortcuts(list());
+ });
+ waitOnMainThread();
+
+ assertCallbackNotReceived(c0_1);
+ assertCallbackNotReceived(c0_3);
+ assertCallbackNotReceived(c0_4);
+ assertCallbackNotReceived(c10_1);
+ assertCallbackNotReceived(c10_2);
+ assertCallbackNotReceived(c11_1);
+ assertCallbackReceived(c0_2, HANDLE_USER_0, CALLING_PACKAGE_3, "s1", "s2", "s3", "s4");
+ assertCallbackReceived(cP0_1, HANDLE_USER_0, CALLING_PACKAGE_3,
+ "s1", "s2", "s3", "s4", "s5", "s6");
+
+ // Work profile.
+ resetAll(all);
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ mManager.removeDynamicShortcuts(list());
+ });
+ waitOnMainThread();
+
+ assertCallbackNotReceived(c0_1);
+ assertCallbackNotReceived(c0_3);
+ assertCallbackNotReceived(c0_4);
+ assertCallbackNotReceived(c10_1);
+ assertCallbackNotReceived(c10_2);
+ assertCallbackNotReceived(c11_1);
+ assertCallbackReceived(c0_2, HANDLE_USER_P0, CALLING_PACKAGE_1, "s1", "s2", "s3", "s5");
+ assertCallbackReceived(cP0_1, HANDLE_USER_P0, CALLING_PACKAGE_1, "s1", "s2", "s3", "s4");
+
+ // Normal secondary user.
+ mRunningUsers.put(USER_10, true);
+
+ resetAll(all);
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ mManager.removeDynamicShortcuts(list());
+ });
+ waitOnMainThread();
+
+ assertCallbackNotReceived(c0_1);
+ assertCallbackNotReceived(c0_2);
+ assertCallbackNotReceived(c0_3);
+ assertCallbackNotReceived(c0_4);
+ assertCallbackNotReceived(cP0_1);
+ assertCallbackNotReceived(c10_2);
+ assertCallbackNotReceived(c11_1);
+ assertCallbackReceived(c10_1, HANDLE_USER_10, CALLING_PACKAGE_1,
+ "x1", "x2", "x3", "x4", "x5");
+ }
+
+ // === Test for persisting ===
+
+ public void testSaveAndLoadUser_empty() {
+ assertTrue(mManager.setDynamicShortcuts(list()));
+
+ Log.i(TAG, "Saved state");
+ dumpsysOnLogcat();
+ dumpUserFile(0);
+
+ // Restore.
+ mService.saveDirtyInfo();
+ initService();
+
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ }
+
+ /**
+ * Try save and load, also stop/start the user.
+ */
+ public void testSaveAndLoadUser() {
+ // First, create some shortcuts and save.
+ runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
+ final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x16);
+ final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.icon2));
+
+ final ShortcutInfo si1 = makeShortcut(
+ "s1",
+ "title1-1",
+ makeComponent(ShortcutActivity.class),
+ icon1,
+ makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
+ "key1", "val1", "nest", makeBundle("key", 123)),
+ /* weight */ 10);
+
+ final ShortcutInfo si2 = makeShortcut(
+ "s2",
+ "title1-2",
+ /* activity */ null,
+ icon2,
+ makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
+ /* weight */ 12);
+
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2)));
+
+ assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+ assertEquals(2, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
+ final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_16x64);
+ final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.icon2));
+
+ final ShortcutInfo si1 = makeShortcut(
+ "s1",
+ "title2-1",
+ makeComponent(ShortcutActivity.class),
+ icon1,
+ makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
+ "key1", "val1", "nest", makeBundle("key", 123)),
+ /* weight */ 10);
+
+ final ShortcutInfo si2 = makeShortcut(
+ "s2",
+ "title2-2",
+ /* activity */ null,
+ icon2,
+ makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
+ /* weight */ 12);
+
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2)));
+
+ assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+ assertEquals(2, mManager.getRemainingCallCount());
+ });
+
+ mRunningUsers.put(USER_10, true);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x64);
+ final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.icon2));
+
+ final ShortcutInfo si1 = makeShortcut(
+ "s1",
+ "title10-1-1",
+ makeComponent(ShortcutActivity.class),
+ icon1,
+ makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
+ "key1", "val1", "nest", makeBundle("key", 123)),
+ /* weight */ 10);
+
+ final ShortcutInfo si2 = makeShortcut(
+ "s2",
+ "title10-1-2",
+ /* activity */ null,
+ icon2,
+ makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
+ /* weight */ 12);
+
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2)));
+
+ assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+ assertEquals(2, mManager.getRemainingCallCount());
+ });
+
+ mService.getShortcutsForTest().get(UserHandle.USER_SYSTEM).setLauncher(
+ new ComponentName("pkg1", "class"));
+
+ // Restore.
+ mService.saveDirtyInfo();
+ initService();
+
+ // Before the load, the map should be empty.
+ assertEquals(0, mService.getShortcutsForTest().size());
+
+ // this will pre-load the per-user info.
+ mService.handleUnlockUser(UserHandle.USER_SYSTEM);
+
+ // Now it's loaded.
+ assertEquals(1, mService.getShortcutsForTest().size());
+
+ runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
+ assertShortcutIds(assertAllDynamic(assertAllHaveIntents(assertAllHaveIcon(
+ mManager.getDynamicShortcuts()))), "s1", "s2");
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ assertEquals("title1-1", getCallerShortcut("s1").getTitle());
+ assertEquals("title1-2", getCallerShortcut("s2").getTitle());
+ });
+ runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
+ assertShortcutIds(assertAllDynamic(assertAllHaveIntents(assertAllHaveIcon(
+ mManager.getDynamicShortcuts()))), "s1", "s2");
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ assertEquals("title2-1", getCallerShortcut("s1").getTitle());
+ assertEquals("title2-2", getCallerShortcut("s2").getTitle());
+ });
+
+ assertEquals("pkg1", mService.getShortcutsForTest().get(UserHandle.USER_SYSTEM)
+ .getLastKnownLauncher().getPackageName());
+
+ // Start another user
+ mService.handleUnlockUser(USER_10);
+
+ // Now the size is 2.
+ assertEquals(2, mService.getShortcutsForTest().size());
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertShortcutIds(assertAllDynamic(assertAllHaveIntents(assertAllHaveIcon(
+ mManager.getDynamicShortcuts()))), "s1", "s2");
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ assertEquals("title10-1-1", getCallerShortcut("s1").getTitle());
+ assertEquals("title10-1-2", getCallerShortcut("s2").getTitle());
+ });
+ assertNull(mService.getShortcutsForTest().get(USER_10).getLastKnownLauncher());
+
+ // Try stopping the user
+ mService.handleCleanupUser(USER_10);
+
+ // Now it's unloaded.
+ assertEquals(1, mService.getShortcutsForTest().size());
+
+ // TODO Check all other fields
+ }
+
+ public void testCleanupPackage() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s0_1"))));
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s0_2"))));
+ });
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s0_1"),
+ HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s0_2"),
+ HANDLE_USER_0);
+ });
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s0_1"),
+ HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s0_2"),
+ HANDLE_USER_0);
+ });
+
+ mRunningUsers.put(USER_10, true);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s10_1"))));
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s10_2"))));
+ });
+ runWithCaller(LAUNCHER_1, USER_10, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s10_1"),
+ HANDLE_USER_10);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s10_2"),
+ HANDLE_USER_10);
+ });
+ runWithCaller(LAUNCHER_2, USER_10, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s10_1"),
+ HANDLE_USER_10);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s10_2"),
+ HANDLE_USER_10);
+ });
+
+ // Remove all dynamic shortcuts; now all shortcuts are just pinned.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.removeAllDynamicShortcuts();
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ mManager.removeAllDynamicShortcuts();
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ mManager.removeAllDynamicShortcuts();
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ mManager.removeAllDynamicShortcuts();
+ });
+
+
+ final SparseArray<ShortcutUser> users = mService.getShortcutsForTest();
+ assertEquals(2, users.size());
+ assertEquals(USER_0, users.keyAt(0));
+ assertEquals(USER_10, users.keyAt(1));
+
+ final ShortcutUser user0 = users.get(USER_0);
+ final ShortcutUser user10 = users.get(USER_10);
+
+
+ // Check the registered packages.
+ dumpsysOnLogcat();
+ assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+ hashSet(user0.getAllPackagesForTest().keySet()));
+ assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+ hashSet(user10.getAllPackagesForTest().keySet()));
+ assertEquals(
+ set(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ hashSet(user0.getAllLaunchersForTest().keySet()));
+ assertEquals(
+ set(PackageWithUser.of(USER_10, LAUNCHER_1),
+ PackageWithUser.of(USER_10, LAUNCHER_2)),
+ hashSet(user10.getAllLaunchersForTest().keySet()));
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
+ "s0_1", "s0_2");
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
+ "s0_1", "s0_2");
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_10),
+ "s10_1", "s10_2");
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_10),
+ "s10_1", "s10_2");
+ assertShortcutExists(CALLING_PACKAGE_1, "s0_1", USER_0);
+ assertShortcutExists(CALLING_PACKAGE_2, "s0_2", USER_0);
+ assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10);
+ assertShortcutExists(CALLING_PACKAGE_2, "s10_2", USER_10);
+
+ mService.saveDirtyInfo();
+
+ // Nonexistent package.
+ uninstallPackage(USER_0, "abc");
+ mService.cleanUpPackageLocked("abc", USER_0, USER_0, /* appStillExists = */ false);
+
+ // No changes.
+ assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+ hashSet(user0.getAllPackagesForTest().keySet()));
+ assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+ hashSet(user10.getAllPackagesForTest().keySet()));
+ assertEquals(
+ set(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ hashSet(user0.getAllLaunchersForTest().keySet()));
+ assertEquals(
+ set(PackageWithUser.of(USER_10, LAUNCHER_1),
+ PackageWithUser.of(USER_10, LAUNCHER_2)),
+ hashSet(user10.getAllLaunchersForTest().keySet()));
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
+ "s0_1", "s0_2");
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
+ "s0_1", "s0_2");
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_10),
+ "s10_1", "s10_2");
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_10),
+ "s10_1", "s10_2");
+ assertShortcutExists(CALLING_PACKAGE_1, "s0_1", USER_0);
+ assertShortcutExists(CALLING_PACKAGE_2, "s0_2", USER_0);
+ assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10);
+ assertShortcutExists(CALLING_PACKAGE_2, "s10_2", USER_10);
+
+ mService.saveDirtyInfo();
+
+ // Remove a package.
+ uninstallPackage(USER_0, CALLING_PACKAGE_1);
+ mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_0, USER_0,
+ /* appStillExists = */ false);
+
+ assertEquals(set(CALLING_PACKAGE_2),
+ hashSet(user0.getAllPackagesForTest().keySet()));
+ assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+ hashSet(user10.getAllPackagesForTest().keySet()));
+ assertEquals(
+ set(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ hashSet(user0.getAllLaunchersForTest().keySet()));
+ assertEquals(
+ set(PackageWithUser.of(USER_10, LAUNCHER_1),
+ PackageWithUser.of(USER_10, LAUNCHER_2)),
+ hashSet(user10.getAllLaunchersForTest().keySet()));
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
+ "s0_2");
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
+ "s0_2");
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_10),
+ "s10_1", "s10_2");
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_10),
+ "s10_1", "s10_2");
+ assertShortcutNotExists(CALLING_PACKAGE_1, "s0_1", USER_0);
+ assertShortcutExists(CALLING_PACKAGE_2, "s0_2", USER_0);
+ assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10);
+ assertShortcutExists(CALLING_PACKAGE_2, "s10_2", USER_10);
+
+ mService.saveDirtyInfo();
+
+ // Remove a launcher.
+ uninstallPackage(USER_10, LAUNCHER_1);
+ mService.cleanUpPackageLocked(LAUNCHER_1, USER_10, USER_10, /* appStillExists = */ false);
+
+ assertEquals(set(CALLING_PACKAGE_2),
+ hashSet(user0.getAllPackagesForTest().keySet()));
+ assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+ hashSet(user10.getAllPackagesForTest().keySet()));
+ assertEquals(
+ set(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ hashSet(user0.getAllLaunchersForTest().keySet()));
+ assertEquals(
+ set(PackageWithUser.of(USER_10, LAUNCHER_2)),
+ hashSet(user10.getAllLaunchersForTest().keySet()));
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
+ "s0_2");
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
+ "s0_2");
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_10),
+ "s10_1", "s10_2");
+ assertShortcutNotExists(CALLING_PACKAGE_1, "s0_1", USER_0);
+ assertShortcutExists(CALLING_PACKAGE_2, "s0_2", USER_0);
+ assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10);
+ assertShortcutExists(CALLING_PACKAGE_2, "s10_2", USER_10);
+
+ mService.saveDirtyInfo();
+
+ // Remove a package.
+ uninstallPackage(USER_10, CALLING_PACKAGE_2);
+ mService.cleanUpPackageLocked(CALLING_PACKAGE_2, USER_10, USER_10,
+ /* appStillExists = */ false);
+
+ assertEquals(set(CALLING_PACKAGE_2),
+ hashSet(user0.getAllPackagesForTest().keySet()));
+ assertEquals(set(CALLING_PACKAGE_1),
+ hashSet(user10.getAllPackagesForTest().keySet()));
+ assertEquals(
+ set(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ hashSet(user0.getAllLaunchersForTest().keySet()));
+ assertEquals(
+ set(PackageWithUser.of(USER_10, LAUNCHER_2)),
+ hashSet(user10.getAllLaunchersForTest().keySet()));
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
+ "s0_2");
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
+ "s0_2");
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_10),
+ "s10_1");
+ assertShortcutNotExists(CALLING_PACKAGE_1, "s0_1", USER_0);
+ assertShortcutExists(CALLING_PACKAGE_2, "s0_2", USER_0);
+ assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10);
+ assertShortcutNotExists(CALLING_PACKAGE_2, "s10_2", USER_10);
+
+ mService.saveDirtyInfo();
+
+ // Remove the other launcher from user 10 too.
+ uninstallPackage(USER_10, LAUNCHER_2);
+ mService.cleanUpPackageLocked(LAUNCHER_2, USER_10, USER_10,
+ /* appStillExists = */ false);
+
+ assertEquals(set(CALLING_PACKAGE_2),
+ hashSet(user0.getAllPackagesForTest().keySet()));
+ assertEquals(set(CALLING_PACKAGE_1),
+ hashSet(user10.getAllPackagesForTest().keySet()));
+ assertEquals(
+ set(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ hashSet(user0.getAllLaunchersForTest().keySet()));
+ assertEquals(
+ set(),
+ hashSet(user10.getAllLaunchersForTest().keySet()));
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
+ "s0_2");
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
+ "s0_2");
+
+ // Note the pinned shortcuts on user-10 no longer referred, so they should both be removed.
+ assertShortcutNotExists(CALLING_PACKAGE_1, "s0_1", USER_0);
+ assertShortcutExists(CALLING_PACKAGE_2, "s0_2", USER_0);
+ assertShortcutNotExists(CALLING_PACKAGE_1, "s10_1", USER_10);
+ assertShortcutNotExists(CALLING_PACKAGE_2, "s10_2", USER_10);
+
+ mService.saveDirtyInfo();
+
+ // More remove.
+ uninstallPackage(USER_10, CALLING_PACKAGE_1);
+ mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_10, USER_10,
+ /* appStillExists = */ false);
+
+ assertEquals(set(CALLING_PACKAGE_2),
+ hashSet(user0.getAllPackagesForTest().keySet()));
+ assertEquals(set(),
+ hashSet(user10.getAllPackagesForTest().keySet()));
+ assertEquals(
+ set(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ hashSet(user0.getAllLaunchersForTest().keySet()));
+ assertEquals(set(),
+ hashSet(user10.getAllLaunchersForTest().keySet()));
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
+ "s0_2");
+ assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
+ "s0_2");
+
+ // Note the pinned shortcuts on user-10 no longer referred, so they should both be removed.
+ assertShortcutNotExists(CALLING_PACKAGE_1, "s0_1", USER_0);
+ assertShortcutExists(CALLING_PACKAGE_2, "s0_2", USER_0);
+ assertShortcutNotExists(CALLING_PACKAGE_1, "s10_1", USER_10);
+ assertShortcutNotExists(CALLING_PACKAGE_2, "s10_2", USER_10);
+
+ mService.saveDirtyInfo();
+ }
+
+ public void testCleanupPackage_republishManifests() {
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s2", "s3", "ms1", "ms2"), HANDLE_USER_0);
+ });
+
+ // Remove ms2 from manifest.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"))));
+
+ // Make sure the shortcuts are in the intended state.
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1", "ms2", "s1", "s2", "s3")
+
+ .selectByIds("ms1")
+ .areAllManifest()
+ .areAllPinned()
+
+ .revertToOriginalList()
+ .selectByIds("ms2")
+ .areAllNotManifest()
+ .areAllPinned()
+
+ .revertToOriginalList()
+ .selectByIds("s1")
+ .areAllDynamic()
+ .areAllNotPinned()
+
+ .revertToOriginalList()
+ .selectByIds("s2")
+ .areAllDynamic()
+ .areAllPinned()
+
+ .revertToOriginalList()
+ .selectByIds("s3")
+ .areAllNotDynamic()
+ .areAllPinned();
+ });
+
+ // Clean up + re-publish manifests.
+ mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_0, USER_0,
+ /* appStillExists = */ true);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1")
+ .areAllManifest();
+ });
+ }
+
+ public void testHandleGonePackage_crossProfile() {
+ // Create some shortcuts.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+
+ mRunningUsers.put(USER_10, true);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ // Pin some.
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s1"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s2"), UserHandle.of(USER_P0));
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s3"), HANDLE_USER_0);
+ });
+
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s2"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s3"), UserHandle.of(USER_P0));
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s1"), HANDLE_USER_0);
+ });
+
+ runWithCaller(LAUNCHER_1, USER_10, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s3"), HANDLE_USER_10);
+ });
+
+ // Check the state.
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ // Make sure all the information is persisted.
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_0);
+ mService.handleUnlockUser(USER_P0);
+ mService.handleUnlockUser(USER_10);
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ // Start uninstalling.
+ uninstallPackage(USER_10, LAUNCHER_1);
+ mService.checkPackageChanges(USER_10);
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ // Uninstall.
+ uninstallPackage(USER_10, CALLING_PACKAGE_1);
+ mService.checkPackageChanges(USER_10);
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ uninstallPackage(USER_P0, LAUNCHER_1);
+ mService.checkPackageChanges(USER_0);
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ mService.checkPackageChanges(USER_P0);
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ uninstallPackage(USER_P0, CALLING_PACKAGE_1);
+
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_0);
+ mService.handleUnlockUser(USER_P0);
+ mService.handleUnlockUser(USER_10);
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ // Uninstall
+ uninstallPackage(USER_0, LAUNCHER_1);
+
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_0);
+ mService.handleUnlockUser(USER_P0);
+ mService.handleUnlockUser(USER_10);
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ uninstallPackage(USER_0, CALLING_PACKAGE_2);
+
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_0);
+ mService.handleUnlockUser(USER_P0);
+ mService.handleUnlockUser(USER_10);
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+ }
+
+ protected void checkCanRestoreTo(boolean expected, ShortcutPackageInfo spi,
+ int version, String... signatures) {
+ assertEquals(expected, spi.canRestoreTo(mService, genPackage(
+ "dummy", /* uid */ 0, version, signatures)));
+ }
+
+ public void testCanRestoreTo() {
+ addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sig1");
+ addPackage(CALLING_PACKAGE_2, CALLING_UID_1, 10, "sig1", "sig2");
+
+ final ShortcutPackageInfo spi1 = ShortcutPackageInfo.generateForInstalledPackage(
+ mService, CALLING_PACKAGE_1, USER_0);
+ final ShortcutPackageInfo spi2 = ShortcutPackageInfo.generateForInstalledPackage(
+ mService, CALLING_PACKAGE_2, USER_0);
+
+ checkCanRestoreTo(true, spi1, 10, "sig1");
+ checkCanRestoreTo(true, spi1, 10, "x", "sig1");
+ checkCanRestoreTo(true, spi1, 10, "sig1", "y");
+ checkCanRestoreTo(true, spi1, 10, "x", "sig1", "y");
+ checkCanRestoreTo(true, spi1, 11, "sig1");
+
+ checkCanRestoreTo(false, spi1, 10 /* empty */);
+ checkCanRestoreTo(false, spi1, 10, "x");
+ checkCanRestoreTo(false, spi1, 10, "x", "y");
+ checkCanRestoreTo(false, spi1, 10, "x");
+ checkCanRestoreTo(false, spi1, 9, "sig1");
+
+ checkCanRestoreTo(true, spi2, 10, "sig1", "sig2");
+ checkCanRestoreTo(true, spi2, 10, "sig2", "sig1");
+ checkCanRestoreTo(true, spi2, 10, "x", "sig1", "sig2");
+ checkCanRestoreTo(true, spi2, 10, "x", "sig2", "sig1");
+ checkCanRestoreTo(true, spi2, 10, "sig1", "sig2", "y");
+ checkCanRestoreTo(true, spi2, 10, "sig2", "sig1", "y");
+ checkCanRestoreTo(true, spi2, 10, "x", "sig1", "sig2", "y");
+ checkCanRestoreTo(true, spi2, 10, "x", "sig2", "sig1", "y");
+ checkCanRestoreTo(true, spi2, 11, "x", "sig2", "sig1", "y");
+
+ checkCanRestoreTo(false, spi2, 10, "sig1", "sig2x");
+ checkCanRestoreTo(false, spi2, 10, "sig2", "sig1x");
+ checkCanRestoreTo(false, spi2, 10, "x", "sig1x", "sig2");
+ checkCanRestoreTo(false, spi2, 10, "x", "sig2x", "sig1");
+ checkCanRestoreTo(false, spi2, 10, "sig1", "sig2x", "y");
+ checkCanRestoreTo(false, spi2, 10, "sig2", "sig1x", "y");
+ checkCanRestoreTo(false, spi2, 10, "x", "sig1x", "sig2", "y");
+ checkCanRestoreTo(false, spi2, 10, "x", "sig2x", "sig1", "y");
+ checkCanRestoreTo(false, spi2, 11, "x", "sig2x", "sig1", "y");
+ }
+
+ public void testHandlePackageDelete() {
+ final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.black_32x32));
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ assertTrue(mManager.addDynamicShortcuts(list(
+ makeShortcutWithIcon("s1", bmp32x32), makeShortcutWithIcon("s2", bmp32x32)
+ )));
+ // Also add a manifest shortcut, which should be removed too.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("s1", "s2", "ms1")
+
+ .selectManifest()
+ .haveIds("ms1");
+ });
+
+ setCaller(CALLING_PACKAGE_2, USER_0);
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
+
+ setCaller(CALLING_PACKAGE_3, USER_0);
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
+
+ mRunningUsers.put(USER_10, true);
+
+ setCaller(CALLING_PACKAGE_1, USER_10);
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
+
+ setCaller(CALLING_PACKAGE_2, USER_10);
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
+
+ setCaller(CALLING_PACKAGE_3, USER_10);
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
+
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
+
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_10));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
+
+ uninstallPackage(USER_0, CALLING_PACKAGE_1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageDeleteIntent(CALLING_PACKAGE_1, USER_0));
+
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
+
+ assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_10));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
+
+ mRunningUsers.put(USER_10, true);
+
+ uninstallPackage(USER_10, CALLING_PACKAGE_2);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageDeleteIntent(CALLING_PACKAGE_2, USER_10));
+
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
+
+ assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_10));
+ assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
+
+ mInjectedPackages.remove(CALLING_PACKAGE_1);
+ mInjectedPackages.remove(CALLING_PACKAGE_3);
+
+ mService.handleUnlockUser(USER_0);
+
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
+
+ assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_0));
+ assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_10));
+ assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
+
+ mService.handleUnlockUser(USER_10);
+
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
+
+ assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_0));
+ assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_0));
+ assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_10));
+ assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
+ assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
+ }
+
+ /** Almost ame as testHandlePackageDelete, except it doesn't uninstall packages. */
+ public void testHandlePackageClearData() {
+ final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.black_32x32));
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ assertTrue(mManager.addDynamicShortcuts(list(
+ makeShortcutWithIcon("s1", bmp32x32), makeShortcutWithIcon("s2", bmp32x32)
+ )));
+
+ setCaller(CALLING_PACKAGE_2, USER_0);
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
+
+ setCaller(CALLING_PACKAGE_3, USER_0);
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
+
+ mRunningUsers.put(USER_10, true);
+
+ setCaller(CALLING_PACKAGE_1, USER_10);
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
+
+ setCaller(CALLING_PACKAGE_2, USER_10);
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
+
+ setCaller(CALLING_PACKAGE_3, USER_10);
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
+
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
+
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_10));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
+
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageDataClear(CALLING_PACKAGE_1, USER_0));
+
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
+
+ assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_10));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
+
+ mRunningUsers.put(USER_10, true);
+
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageDataClear(CALLING_PACKAGE_2, USER_10));
+
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
+
+ assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_10));
+ assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
+ }
+
+ public void testHandlePackageClearData_manifestRepublished() {
+
+ mRunningUsers.put(USER_10, true);
+
+ // Add two manifests and two dynamics.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_10));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.addDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"))));
+ });
+ runWithCaller(LAUNCHER_1, USER_10, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms2", "s2"), HANDLE_USER_10);
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1", "ms2", "s1", "s2")
+ .areAllEnabled()
+
+ .selectPinned()
+ .haveIds("ms2", "s2");
+ });
+
+ // Clear data
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageDataClear(CALLING_PACKAGE_1, USER_10));
+
+ // Only manifest shortcuts will remain, and are no longer pinned.
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1", "ms2")
+ .areAllEnabled()
+ .areAllNotPinned();
+ });
+ }
+
+ public void testHandlePackageUpdate() throws Throwable {
+ // Set up shortcuts and launchers.
+
+ final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
+ final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.black_32x32));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"),
+ makeShortcutWithIcon("s2", res32x32),
+ makeShortcutWithIcon("s3", res32x32),
+ makeShortcutWithIcon("s4", bmp32x32))));
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"),
+ makeShortcutWithIcon("s2", bmp32x32))));
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcutWithIcon("s1", res32x32))));
+ });
+
+ mRunningUsers.put(USER_10, true);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcutWithIcon("s1", res32x32),
+ makeShortcutWithIcon("s2", res32x32))));
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcutWithIcon("s1", bmp32x32),
+ makeShortcutWithIcon("s2", bmp32x32))));
+ });
+
+ LauncherApps.Callback c0 = mock(LauncherApps.Callback.class);
+ LauncherApps.Callback c10 = mock(LauncherApps.Callback.class);
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.registerCallback(c0, new Handler(Looper.getMainLooper()));
+ });
+ runWithCaller(LAUNCHER_1, USER_10, () -> {
+ mLauncherApps.registerCallback(c10, new Handler(Looper.getMainLooper()));
+ });
+
+ mInjectedCurrentTimeMillis = START_TIME + 100;
+
+ ArgumentCaptor<List> shortcuts;
+
+ // Update the version info for package 1.
+ reset(c0);
+ reset(c10);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+
+ // Then send the broadcast, to only user-0.
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
+
+ waitOnMainThread();
+
+ // User-0 should get the notification.
+ shortcuts = ArgumentCaptor.forClass(List.class);
+ verify(c0).onShortcutsChanged(
+ eq(CALLING_PACKAGE_1),
+ shortcuts.capture(),
+ eq(HANDLE_USER_0));
+
+ // User-10 shouldn't yet get the notification.
+ verify(c10, times(0)).onShortcutsChanged(
+ eq(CALLING_PACKAGE_1),
+ any(List.class),
+ any(UserHandle.class));
+ assertShortcutIds(shortcuts.getValue(), "s1", "s2", "s3", "s4");
+ assertEquals(START_TIME,
+ findShortcut(shortcuts.getValue(), "s1").getLastChangedTimestamp());
+ assertEquals(START_TIME + 100,
+ findShortcut(shortcuts.getValue(), "s2").getLastChangedTimestamp());
+ assertEquals(START_TIME + 100,
+ findShortcut(shortcuts.getValue(), "s3").getLastChangedTimestamp());
+ assertEquals(START_TIME,
+ findShortcut(shortcuts.getValue(), "s4").getLastChangedTimestamp());
+
+ // Next, send unlock even on user-10. Now we scan packages on this user and send a
+ // notification to the launcher.
+ mInjectedCurrentTimeMillis = START_TIME + 200;
+
+ mRunningUsers.put(USER_10, true);
+
+ reset(c0);
+ reset(c10);
+ mService.handleUnlockUser(USER_10);
+
+ waitOnMainThread();
+
+ shortcuts = ArgumentCaptor.forClass(List.class);
+ verify(c0, times(0)).onShortcutsChanged(
+ eq(CALLING_PACKAGE_1),
+ any(List.class),
+ any(UserHandle.class));
+
+ verify(c10).onShortcutsChanged(
+ eq(CALLING_PACKAGE_1),
+ shortcuts.capture(),
+ eq(HANDLE_USER_10));
+
+ assertShortcutIds(shortcuts.getValue(), "s1", "s2");
+ assertEquals(START_TIME + 200,
+ findShortcut(shortcuts.getValue(), "s1").getLastChangedTimestamp());
+ assertEquals(START_TIME + 200,
+ findShortcut(shortcuts.getValue(), "s2").getLastChangedTimestamp());
+
+
+ // Do the same thing for package 2, which doesn't have resource icons.
+ mInjectedCurrentTimeMillis = START_TIME + 300;
+
+ reset(c0);
+ reset(c10);
+ updatePackageVersion(CALLING_PACKAGE_2, 10);
+
+ // Then send the broadcast, to only user-0.
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageUpdateIntent(CALLING_PACKAGE_2, USER_0));
+ mService.handleUnlockUser(USER_10);
+
+ waitOnMainThread();
+
+ verify(c0, times(0)).onShortcutsChanged(
+ eq(CALLING_PACKAGE_1),
+ any(List.class),
+ any(UserHandle.class));
+
+ verify(c10, times(0)).onShortcutsChanged(
+ eq(CALLING_PACKAGE_1),
+ any(List.class),
+ any(UserHandle.class));
+
+ // Do the same thing for package 3
+ mInjectedCurrentTimeMillis = START_TIME + 400;
+
+ reset(c0);
+ reset(c10);
+ updatePackageVersion(CALLING_PACKAGE_3, 100);
+
+ // Then send the broadcast, to only user-0.
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageUpdateIntent(CALLING_PACKAGE_3, USER_0));
+ mService.handleUnlockUser(USER_10);
+
+ waitOnMainThread();
+
+ shortcuts = ArgumentCaptor.forClass(List.class);
+ verify(c0).onShortcutsChanged(
+ eq(CALLING_PACKAGE_3),
+ shortcuts.capture(),
+ eq(HANDLE_USER_0));
+
+ // User 10 doesn't have package 3, so no callback.
+ verify(c10, times(0)).onShortcutsChanged(
+ eq(CALLING_PACKAGE_3),
+ any(List.class),
+ any(UserHandle.class));
+
+ assertShortcutIds(shortcuts.getValue(), "s1");
+ assertEquals(START_TIME + 400,
+ findShortcut(shortcuts.getValue(), "s1").getLastChangedTimestamp());
+ }
+
+ /**
+ * Test the case where an updated app has resource IDs changed.
+ */
+ public void testHandlePackageUpdate_resIdChanged() throws Exception {
+ final Icon icon1 = Icon.createWithResource(getTestContext(), /* res ID */ 1000);
+ final Icon icon2 = Icon.createWithResource(getTestContext(), /* res ID */ 1001);
+
+ // Set up shortcuts.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ // Note resource strings are not officially supported (they're hidden), but
+ // should work.
+
+ final ShortcutInfo s1 = new ShortcutInfo.Builder(mClientContext)
+ .setId("s1")
+ .setActivity(makeComponent(ShortcutActivity.class))
+ .setIntent(new Intent(Intent.ACTION_VIEW))
+ .setIcon(icon1)
+ .setTitleResId(10000)
+ .setTextResId(10001)
+ .setDisabledMessageResId(10002)
+ .build();
+
+ final ShortcutInfo s2 = new ShortcutInfo.Builder(mClientContext)
+ .setId("s2")
+ .setActivity(makeComponent(ShortcutActivity.class))
+ .setIntent(new Intent(Intent.ACTION_VIEW))
+ .setIcon(icon2)
+ .setTitleResId(20000)
+ .build();
+
+ assertTrue(mManager.setDynamicShortcuts(list(s1, s2)));
+ });
+
+ // Verify.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ final ShortcutInfo s1 = getCallerShortcut("s1");
+ final ShortcutInfo s2 = getCallerShortcut("s2");
+
+ assertEquals(1000, s1.getIconResourceId());
+ assertEquals(10000, s1.getTitleResId());
+ assertEquals(10001, s1.getTextResId());
+ assertEquals(10002, s1.getDisabledMessageResourceId());
+
+ assertEquals(1001, s2.getIconResourceId());
+ assertEquals(20000, s2.getTitleResId());
+ assertEquals(0, s2.getTextResId());
+ assertEquals(0, s2.getDisabledMessageResourceId());
+ });
+
+ mService.saveDirtyInfo();
+ initService();
+
+ // Set up the mock resources again, with an "adjustment".
+ // When the package is updated, the service will fetch the updated res-IDs with res-names,
+ // and the new IDs will have this offset.
+ setUpAppResources(10);
+
+ // Update the package.
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ final ShortcutInfo s1 = getCallerShortcut("s1");
+ final ShortcutInfo s2 = getCallerShortcut("s2");
+
+ assertEquals(1010, s1.getIconResourceId());
+ assertEquals(10010, s1.getTitleResId());
+ assertEquals(10011, s1.getTextResId());
+ assertEquals(10012, s1.getDisabledMessageResourceId());
+
+ assertEquals(1011, s2.getIconResourceId());
+ assertEquals(20010, s2.getTitleResId());
+ assertEquals(0, s2.getTextResId());
+ assertEquals(0, s2.getDisabledMessageResourceId());
+ });
+ }
+
+ public void testHandlePackageChanged() {
+ final ComponentName ACTIVITY1 = new ComponentName(CALLING_PACKAGE_1, "act1");
+ final ComponentName ACTIVITY2 = new ComponentName(CALLING_PACKAGE_1, "act2");
+
+ addManifestShortcutResource(ACTIVITY1, R.xml.shortcut_1);
+ addManifestShortcutResource(ACTIVITY2, R.xml.shortcut_1_alt);
+
+ mRunningUsers.put(USER_10, true);
+
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_10));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.addDynamicShortcuts(list(
+ makeShortcutWithActivity("s1", ACTIVITY1),
+ makeShortcutWithActivity("s2", ACTIVITY2)
+ )));
+ });
+ runWithCaller(LAUNCHER_1, USER_10, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms1-alt", "s2"), HANDLE_USER_10);
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1", "ms1-alt", "s1", "s2")
+ .areAllEnabled()
+
+ .selectPinned()
+ .haveIds("ms1-alt", "s2")
+
+ .revertToOriginalList()
+ .selectByIds("ms1", "s1")
+ .areAllWithActivity(ACTIVITY1)
+
+ .revertToOriginalList()
+ .selectByIds("ms1-alt", "s2")
+ .areAllWithActivity(ACTIVITY2)
+ ;
+ });
+
+ // First, no changes.
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageChangedIntent(CALLING_PACKAGE_1, USER_10));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1", "ms1-alt", "s1", "s2")
+ .areAllEnabled()
+
+ .selectPinned()
+ .haveIds("ms1-alt", "s2")
+
+ .revertToOriginalList()
+ .selectByIds("ms1", "s1")
+ .areAllWithActivity(ACTIVITY1)
+
+ .revertToOriginalList()
+ .selectByIds("ms1-alt", "s2")
+ .areAllWithActivity(ACTIVITY2)
+ ;
+ });
+
+ // Disable activity 1
+ mEnabledActivityChecker = (activity, userId) -> !ACTIVITY1.equals(activity);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageChangedIntent(CALLING_PACKAGE_1, USER_10));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1-alt", "s2")
+ .areAllEnabled()
+
+ .selectPinned()
+ .haveIds("ms1-alt", "s2")
+
+ .revertToOriginalList()
+ .selectByIds("ms1-alt", "s2")
+ .areAllWithActivity(ACTIVITY2)
+ ;
+ });
+
+ // Re-enable activity 1.
+ // Manifest shortcuts will be re-published, but dynamic ones are not.
+ mEnabledActivityChecker = (activity, userId) -> true;
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageChangedIntent(CALLING_PACKAGE_1, USER_10));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1", "ms1-alt", "s2")
+ .areAllEnabled()
+
+ .selectPinned()
+ .haveIds("ms1-alt", "s2")
+
+ .revertToOriginalList()
+ .selectByIds("ms1")
+ .areAllWithActivity(ACTIVITY1)
+
+ .revertToOriginalList()
+ .selectByIds("ms1-alt", "s2")
+ .areAllWithActivity(ACTIVITY2)
+ ;
+ });
+
+ // Disable activity 2
+ // Because "ms1-alt" and "s2" are both pinned, they will remain, but disabled.
+ mEnabledActivityChecker = (activity, userId) -> !ACTIVITY2.equals(activity);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageChangedIntent(CALLING_PACKAGE_1, USER_10));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1", "ms1-alt", "s2")
+
+ .selectDynamic().isEmpty().revertToOriginalList() // no dynamics.
+
+ .selectPinned()
+ .haveIds("ms1-alt", "s2")
+ .areAllDisabled()
+
+ .revertToOriginalList()
+ .selectByIds("ms1")
+ .areAllWithActivity(ACTIVITY1)
+ .areAllEnabled()
+ ;
+ });
+ }
+
+ public void testHandlePackageUpdate_activityNoLongerMain() throws Throwable {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcutWithActivity("s1a",
+ new ComponentName(getCallingPackage(), "act1")),
+ makeShortcutWithActivity("s1b",
+ new ComponentName(getCallingPackage(), "act1")),
+ makeShortcutWithActivity("s2a",
+ new ComponentName(getCallingPackage(), "act2")),
+ makeShortcutWithActivity("s2b",
+ new ComponentName(getCallingPackage(), "act2")),
+ makeShortcutWithActivity("s3a",
+ new ComponentName(getCallingPackage(), "act3")),
+ makeShortcutWithActivity("s3b",
+ new ComponentName(getCallingPackage(), "act3"))
+ )));
+ assertWith(getCallerShortcuts())
+ .haveIds("s1a", "s1b", "s2a", "s2b", "s3a", "s3b")
+ .areAllDynamic();
+ });
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s1b", "s2b", "s3b"),
+ HANDLE_USER_0);
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("s1a", "s1b", "s2a", "s2b", "s3a", "s3b")
+ .areAllDynamic()
+
+ .selectByIds("s1b", "s2b", "s3b")
+ .areAllPinned();
+ });
+
+ // Update the app and act2 and act3 are no longer main.
+ mMainActivityChecker = (activity, userId) -> {
+ return activity.getClassName().equals("act1");
+ };
+
+ setCaller(LAUNCHER_1, USER_0);
+ assertForLauncherCallback(mLauncherApps, () -> {
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
+ }).assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_0)
+ // Make sure the launcher gets callbacks.
+ .haveIds("s1a", "s1b", "s2b", "s3b")
+ .areAllWithKeyFieldsOnly();
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ // s2a and s3a are gone, but s2b and s3b will remain because they're pinned, and
+ // disabled.
+ assertWith(getCallerShortcuts())
+ .haveIds("s1a", "s1b", "s2b", "s3b")
+
+ .selectByIds("s1a", "s1b")
+ .areAllDynamic()
+ .areAllEnabled()
+
+ .revertToOriginalList()
+ .selectByIds("s2b", "s3b")
+ .areAllNotDynamic()
+ .areAllDisabled()
+ .areAllPinned()
+ ;
+ });
+ }
+
+ protected void prepareForBackupTest() {
+
+ prepareCrossProfileDataSet();
+
+ backupAndRestore();
+ }
+
+ /**
+ * Make sure the backup data doesn't have the following information:
+ * - Launchers on other users.
+ * - Non-backup app information.
+ *
+ * But restores all other infomation.
+ *
+ * It also omits the following pieces of information, but that's tested in
+ * {@link ShortcutManagerTest2#testShortcutInfoSaveAndLoad_forBackup}.
+ * - Unpinned dynamic shortcuts
+ * - Bitmaps
+ */
+ public void testBackupAndRestore() {
+ prepareForBackupTest();
+
+ checkBackupAndRestore_success();
+ }
+
+ public void testBackupAndRestore_backupRestoreTwice() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ dumpsysOnLogcat("Before second backup");
+
+ backupAndRestore();
+
+ dumpsysOnLogcat("After second backup");
+
+ checkBackupAndRestore_success();
+ }
+
+ public void testBackupAndRestore_backupRestoreMultiple() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ backupAndRestore();
+
+ // This also shouldn't affect the result.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"),
+ makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6"))));
+ });
+
+ backupAndRestore();
+
+ checkBackupAndRestore_success();
+ }
+
+ public void testBackupAndRestore_restoreToNewVersion() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ backupAndRestore();
+
+ addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 2);
+ addPackage(LAUNCHER_1, LAUNCHER_UID_1, 5);
+
+ checkBackupAndRestore_success();
+ }
+
+ public void testBackupAndRestore_restoreToSuperSetSignatures() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ backupAndRestore();
+
+ // Change package signatures.
+ addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 1, "sigx", CALLING_PACKAGE_1);
+ addPackage(LAUNCHER_1, LAUNCHER_UID_1, 4, LAUNCHER_1, "sigy");
+
+ checkBackupAndRestore_success();
+ }
+
+ protected void checkBackupAndRestore_success() {
+ // Make sure non-system user is not restored.
+ final ShortcutUser userP0 = mService.getUserShortcutsLocked(USER_P0);
+ assertEquals(0, userP0.getAllPackagesForTest().size());
+ assertEquals(0, userP0.getAllLaunchersForTest().size());
+
+ // Make sure only "allowBackup" apps are restored, and are shadow.
+ final ShortcutUser user0 = mService.getUserShortcutsLocked(USER_0);
+ assertExistsAndShadow(user0.getAllPackagesForTest().get(CALLING_PACKAGE_1));
+ assertExistsAndShadow(user0.getAllPackagesForTest().get(CALLING_PACKAGE_2));
+ assertExistsAndShadow(user0.getAllLaunchersForTest().get(
+ PackageWithUser.of(USER_0, LAUNCHER_1)));
+ assertExistsAndShadow(user0.getAllLaunchersForTest().get(
+ PackageWithUser.of(USER_0, LAUNCHER_2)));
+
+ assertNull(user0.getAllPackagesForTest().get(CALLING_PACKAGE_3));
+ assertNull(user0.getAllLaunchersForTest().get(PackageWithUser.of(USER_0, LAUNCHER_3)));
+ assertNull(user0.getAllLaunchersForTest().get(PackageWithUser.of(USER_P0, LAUNCHER_1)));
+
+ installPackage(USER_0, CALLING_PACKAGE_1);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerVisibleShortcuts())
+ .selectDynamic()
+ .isEmpty()
+
+ .revertToOriginalList()
+ .selectPinned()
+ .haveIds("s1", "s2");
+ });
+
+ installPackage(USER_0, LAUNCHER_1);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ .areAllPinned()
+ .haveIds("s1");
+
+ assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+ .isEmpty();
+
+ assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ .isEmpty();
+
+ assertWith(mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0))
+ .isEmpty();
+ });
+
+ installPackage(USER_0, CALLING_PACKAGE_2);
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertWith(getCallerVisibleShortcuts())
+ .selectDynamic()
+ .isEmpty()
+
+ .revertToOriginalList()
+ .selectPinned()
+ .haveIds("s1", "s2", "s3");
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ .areAllPinned()
+ .haveIds("s1");
+
+ assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+ .areAllPinned()
+ .haveIds("s1", "s2");
+
+ assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ .isEmpty();
+
+ assertWith(mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0))
+ .isEmpty();
+ });
+
+ // 3 shouldn't be backed up, so no pinned shortcuts.
+ installPackage(USER_0, CALLING_PACKAGE_3);
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertWith(getCallerVisibleShortcuts())
+ .isEmpty();
+ });
+
+ // Launcher on a different profile shouldn't be restored.
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ .isEmpty();
+ assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+ .isEmpty();
+ assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ .isEmpty();
+ });
+
+ // Package on a different profile, no restore.
+ installPackage(USER_P0, CALLING_PACKAGE_1);
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ assertWith(getCallerVisibleShortcuts())
+ .isEmpty();
+ });
+
+ // Restore launcher 2 on user 0.
+ installPackage(USER_0, LAUNCHER_2);
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ .areAllPinned()
+ .haveIds("s2");
+
+ assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+ .areAllPinned()
+ .haveIds("s2", "s3");
+
+ assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ .isEmpty();
+
+ assertWith(mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0))
+ .isEmpty();
+ });
+
+
+ // Restoration of launcher2 shouldn't affect other packages; so do the same checks and
+ // make sure they still have the same result.
+ installPackage(USER_0, CALLING_PACKAGE_1);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerVisibleShortcuts())
+ .areAllPinned()
+ .haveIds("s1", "s2");
+ });
+
+ installPackage(USER_0, LAUNCHER_1);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ .areAllPinned()
+ .haveIds("s1");
+
+ assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+ .areAllPinned()
+ .haveIds("s1", "s2");
+
+ assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ .isEmpty();
+
+ assertWith(mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0))
+ .isEmpty();
+ });
+
+ installPackage(USER_0, CALLING_PACKAGE_2);
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertWith(getCallerVisibleShortcuts())
+ .areAllPinned()
+ .haveIds("s1", "s2", "s3");
+ });
+ }
+
+ public void testBackupAndRestore_publisherLowerVersion() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ backupAndRestore();
+
+ addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 0); // Lower version
+
+ checkBackupAndRestore_publisherNotRestored();
+ }
+
+ public void testBackupAndRestore_publisherWrongSignature() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ backupAndRestore();
+
+ addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sigx"); // different signature
+
+ checkBackupAndRestore_publisherNotRestored();
+ }
+
+ public void testBackupAndRestore_publisherNoLongerBackupTarget() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ backupAndRestore();
+
+ updatePackageInfo(CALLING_PACKAGE_1,
+ pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
+
+ checkBackupAndRestore_publisherNotRestored();
+ }
+
+ protected void checkBackupAndRestore_publisherNotRestored() {
+ installPackage(USER_0, CALLING_PACKAGE_1);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertEquals(0, mManager.getPinnedShortcuts().size());
+ });
+
+ installPackage(USER_0, CALLING_PACKAGE_2);
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s1", "s2", "s3");
+ });
+
+ installPackage(USER_0, LAUNCHER_1);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+ "s1", "s2");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+ installPackage(USER_0, LAUNCHER_2);
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+
+ installPackage(USER_0, CALLING_PACKAGE_3);
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertEquals(0, mManager.getPinnedShortcuts().size());
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+ "s1", "s2");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+ }
+
+ public void testBackupAndRestore_launcherLowerVersion() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ backupAndRestore();
+
+ addPackage(LAUNCHER_1, LAUNCHER_UID_1, 0); // Lower version
+
+ checkBackupAndRestore_launcherNotRestored();
+ }
+
+ public void testBackupAndRestore_launcherWrongSignature() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ backupAndRestore();
+
+ addPackage(LAUNCHER_1, LAUNCHER_UID_1, 10, "sigx"); // different signature
+
+ checkBackupAndRestore_launcherNotRestored();
+ }
+
+ public void testBackupAndRestore_launcherNoLongerBackupTarget() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ backupAndRestore();
+
+ updatePackageInfo(LAUNCHER_1,
+ pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
+
+ checkBackupAndRestore_launcherNotRestored();
+ }
+
+ protected void checkBackupAndRestore_launcherNotRestored() {
+ installPackage(USER_0, CALLING_PACKAGE_1);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+
+ // s1 was pinned by launcher 1, which is not restored, yet, so we still see "s1" here.
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s1", "s2");
+ });
+
+ installPackage(USER_0, CALLING_PACKAGE_2);
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s1", "s2", "s3");
+ });
+
+ // Now we try to restore launcher 1. Then we realize it's not restorable, so L1 has no pinned
+ // shortcuts.
+ installPackage(USER_0, LAUNCHER_1);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+
+ // Now CALLING_PACKAGE_1 realizes "s1" is no longer pinned.
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s2");
+ });
+
+ installPackage(USER_0, LAUNCHER_2);
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)),
+ "s2");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+
+ installPackage(USER_0, CALLING_PACKAGE_3);
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertEquals(0, mManager.getPinnedShortcuts().size());
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)),
+ "s2");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+ }
+
+ public void testBackupAndRestore_launcherAndPackageNoLongerBackupTarget() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ backupAndRestore();
+
+ updatePackageInfo(CALLING_PACKAGE_1,
+ pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
+
+ updatePackageInfo(LAUNCHER_1,
+ pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
+
+ checkBackupAndRestore_publisherAndLauncherNotRestored();
+ }
+
+ protected void checkBackupAndRestore_publisherAndLauncherNotRestored() {
+ installPackage(USER_0, CALLING_PACKAGE_1);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertEquals(0, mManager.getPinnedShortcuts().size());
+ });
+
+ installPackage(USER_0, CALLING_PACKAGE_2);
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s1", "s2", "s3");
+ });
+
+ installPackage(USER_0, LAUNCHER_1);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+ installPackage(USER_0, LAUNCHER_2);
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+
+ // Because launcher 1 wasn't restored, "s1" is no longer pinned.
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s2", "s3");
+ });
+
+ installPackage(USER_0, CALLING_PACKAGE_3);
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertEquals(0, mManager.getPinnedShortcuts().size());
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+ }
+
+ public void testBackupAndRestore_disabled() {
+ prepareCrossProfileDataSet();
+
+ // Before doing backup & restore, disable s1.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.disableShortcuts(list("s1"));
+ });
+
+ backupAndRestore();
+
+ // Below is copied from checkBackupAndRestore_success.
+
+ // Make sure non-system user is not restored.
+ final ShortcutUser userP0 = mService.getUserShortcutsLocked(USER_P0);
+ assertEquals(0, userP0.getAllPackagesForTest().size());
+ assertEquals(0, userP0.getAllLaunchersForTest().size());
+
+ // Make sure only "allowBackup" apps are restored, and are shadow.
+ final ShortcutUser user0 = mService.getUserShortcutsLocked(USER_0);
+ assertExistsAndShadow(user0.getAllPackagesForTest().get(CALLING_PACKAGE_1));
+ assertExistsAndShadow(user0.getAllPackagesForTest().get(CALLING_PACKAGE_2));
+ assertExistsAndShadow(user0.getAllLaunchersForTest().get(
+ PackageWithUser.of(USER_0, LAUNCHER_1)));
+ assertExistsAndShadow(user0.getAllLaunchersForTest().get(
+ PackageWithUser.of(USER_0, LAUNCHER_2)));
+
+ assertNull(user0.getAllPackagesForTest().get(CALLING_PACKAGE_3));
+ assertNull(user0.getAllLaunchersForTest().get(PackageWithUser.of(USER_0, LAUNCHER_3)));
+ assertNull(user0.getAllLaunchersForTest().get(PackageWithUser.of(USER_P0, LAUNCHER_1)));
+
+ installPackage(USER_0, CALLING_PACKAGE_1);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerVisibleShortcuts())
+ .areAllEnabled() // disabled shortcuts shouldn't be restored.
+
+ .selectDynamic()
+ .isEmpty()
+
+ .revertToOriginalList()
+ .selectPinned()
+ // s1 is not restored.
+ .haveIds("s2");
+ });
+
+ installPackage(USER_0, LAUNCHER_1);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ // Note, s1 was pinned by launcher 1, but was disabled, so isn't restored.
+ assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ .isEmpty();
+
+ assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+ .isEmpty();
+
+ assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ .isEmpty();
+
+ assertWith(mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0))
+ .isEmpty();
+ });
+ }
+
+
+ public void testBackupAndRestore_manifestRePublished() {
+ // Publish two manifest shortcuts.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(mServiceContext,
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+
+ // Pin from launcher 1.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("ms1", "ms2", "s1", "s2"), HANDLE_USER_0);
+ });
+
+ // Update and now ms2 is gone -> disabled.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(mServiceContext,
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // Make sure the manifest shortcuts have been published.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .selectManifest()
+ .haveIds("ms1")
+
+ .revertToOriginalList()
+ .selectDynamic()
+ .haveIds("s1", "s2", "s3")
+
+ .revertToOriginalList()
+ .selectPinned()
+ .haveIds("ms1", "ms2", "s1", "s2")
+
+ .revertToOriginalList()
+ .selectByIds("ms1")
+ .areAllManifest()
+ .areAllEnabled()
+
+ .revertToOriginalList()
+ .selectByIds("ms2")
+ .areAllNotManifest()
+ .areAllDisabled();
+ });
+
+ backupAndRestore();
+
+ // When re-installing the app, the manifest shortcut should be re-published.
+ mService.mPackageMonitor.onReceive(mServiceContext,
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+ mService.mPackageMonitor.onReceive(mServiceContext,
+ genPackageAddIntent(LAUNCHER_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerVisibleShortcuts())
+ .selectPinned()
+ // ms2 was disabled, so not restored.
+ .haveIds("ms1", "s1", "s2")
+ .areAllEnabled()
+
+ .revertToOriginalList()
+ .selectByIds("ms1")
+ .areAllManifest()
+
+ .revertToOriginalList()
+ .selectByIds("s1", "s2")
+ .areAllNotDynamic()
+ ;
+ });
+ }
+
+ /**
+ * It's the case with preintalled apps -- when applyRestore() is called, the system
+ * apps are already installed, so manifest shortcuts need to be re-published.
+ */
+ public void testBackupAndRestore_appAlreadyInstalledWhenRestored() {
+ // Pre-backup. Same as testBackupAndRestore_manifestRePublished().
+
+ // Publish two manifest shortcuts.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(mServiceContext,
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+
+ // Pin from launcher 1.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("ms1", "ms2", "s1", "s2"), HANDLE_USER_0);
+ });
+
+ // Update and now ms2 is gone -> disabled.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(mServiceContext,
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // Make sure the manifest shortcuts have been published.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .selectManifest()
+ .haveIds("ms1")
+
+ .revertToOriginalList()
+ .selectDynamic()
+ .haveIds("s1", "s2", "s3")
+
+ .revertToOriginalList()
+ .selectPinned()
+ .haveIds("ms1", "ms2", "s1", "s2")
+
+ .revertToOriginalList()
+ .selectByIds("ms1")
+ .areAllManifest()
+ .areAllEnabled()
+
+ .revertToOriginalList()
+ .selectByIds("ms2")
+ .areAllNotManifest()
+ .areAllDisabled();
+ });
+
+ // Backup and *without restarting the service, just call applyRestore()*.
+ {
+ int prevUid = mInjectedCallingUid;
+ mInjectedCallingUid = Process.SYSTEM_UID; // Only system can call it.
+
+ dumpsysOnLogcat("Before backup");
+
+ final byte[] payload = mService.getBackupPayload(USER_0);
+ if (ENABLE_DUMP) {
+ final String xml = new String(payload);
+ Log.v(TAG, "Backup payload:");
+ for (String line : xml.split("\n")) {
+ Log.v(TAG, line);
+ }
+ }
+ mService.applyRestore(payload, USER_0);
+
+ dumpsysOnLogcat("After restore");
+
+ mInjectedCallingUid = prevUid;
+ }
+
+ // The check is also the same as testBackupAndRestore_manifestRePublished().
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerVisibleShortcuts())
+ .selectPinned()
+ // ms2 was disabled, so not restored.
+ .haveIds("ms1", "s1", "s2")
+ .areAllEnabled()
+
+ .revertToOriginalList()
+ .selectByIds("ms1")
+ .areAllManifest()
+
+ .revertToOriginalList()
+ .selectByIds("s1", "s2")
+ .areAllNotDynamic()
+ ;
+ });
+ }
+
+ public void testSaveAndLoad_crossProfile() {
+ prepareCrossProfileDataSet();
+
+ dumpsysOnLogcat("Before save & load");
+
+ mService.saveDirtyInfo();
+ initService();
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()),
+ "s1", "s2", "s3", "s4");
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()),
+ "s1", "s2", "s3", "s4", "s5");
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()),
+ "s1", "s2", "s3", "s4", "s5", "s6");
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts())
+ /* empty */);
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts())
+ /* empty */);
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()),
+ "s1", "s2", "s3", "s4", "s5", "s6");
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_P0, () -> {
+ assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts())
+ /* empty */);
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts())
+ /* empty */);
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()),
+ "x1", "x2", "x3");
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()),
+ "x4", "x5");
+ });
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_0),
+ "s1");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_0),
+ "s1", "s2");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_0),
+ "s1", "s2", "s3");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_4), HANDLE_USER_0)
+ /* empty */);
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0),
+ "s1", "s4");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_P0)
+ /* empty */);
+ assertExpectException(
+ SecurityException.class, "", () -> {
+ mLauncherApps.getShortcuts(
+ buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_10);
+ });
+ });
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_0),
+ "s2");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_0),
+ "s2", "s3");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_0),
+ "s2", "s3", "s4");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_4), HANDLE_USER_0)
+ /* empty */);
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0),
+ "s2", "s5");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_P0)
+ /* empty */);
+ });
+ runWithCaller(LAUNCHER_3, USER_0, () -> {
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_0),
+ "s3");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_0),
+ "s3", "s4");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_0),
+ "s3", "s4", "s5");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_4), HANDLE_USER_0)
+ /* empty */);
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0),
+ "s3", "s6");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_P0)
+ /* empty */);
+ });
+ runWithCaller(LAUNCHER_4, USER_0, () -> {
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_0)
+ /* empty */);
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_0)
+ /* empty */);
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_0)
+ /* empty */);
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_4), HANDLE_USER_0)
+ /* empty */);
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0)
+ /* empty */);
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_P0)
+ /* empty */);
+ });
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_0),
+ "s3", "s4");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_0),
+ "s3", "s4", "s5");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_0),
+ "s3", "s4", "s5", "s6");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0),
+ "s1", "s4");
+ assertExpectException(
+ SecurityException.class, "unrelated profile", () -> {
+ mLauncherApps.getShortcuts(
+ buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_10);
+ });
+ });
+ runWithCaller(LAUNCHER_1, USER_10, () -> {
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_10),
+ "x4", "x5");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_10)
+ /* empty */);
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_10)
+ /* empty */);
+ assertExpectException(
+ SecurityException.class, "unrelated profile", () -> {
+ mLauncherApps.getShortcuts(
+ buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0);
+ });
+ assertExpectException(
+ SecurityException.class, "unrelated profile", () -> {
+ mLauncherApps.getShortcuts(
+ buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_P0);
+ });
+ });
+ }
+
+ public void testOnApplicationActive_permission() {
+ assertExpectException(SecurityException.class, "Missing permission", () ->
+ mManager.onApplicationActive(CALLING_PACKAGE_1, USER_0));
+
+ // Has permission, now it should pass.
+ mCallerPermissions.add(permission.RESET_SHORTCUT_MANAGER_THROTTLING);
+ mManager.onApplicationActive(CALLING_PACKAGE_1, USER_0);
+ }
+
+ public void testDumpsys_crossProfile() {
+ prepareCrossProfileDataSet();
+ dumpsysOnLogcat("test1", /* force= */ true);
+ }
+
+ public void testDumpsys_withIcons() throws IOException {
+ testIcons();
+ // Dump after having some icons.
+ dumpsysOnLogcat("test1", /* force= */ true);
+ }
+
+ public void testManifestShortcut_publishOnUnlockUser() {
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_3, ShortcutActivity.class.getName()),
+ R.xml.shortcut_5);
+
+ // Unlock user-0.
+ mService.handleUnlockUser(USER_0);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2", "ms3", "ms4", "ms5");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ // Try on another user, with some packages uninstalled.
+ mRunningUsers.put(USER_10, true);
+
+ uninstallPackage(USER_10, CALLING_PACKAGE_1);
+ uninstallPackage(USER_10, CALLING_PACKAGE_3);
+
+ mService.handleUnlockUser(USER_10);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_3, USER_10, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ // Now change the resources for package 1, and unlock again.
+ // But we still see *old* shortcuts, because the package version and install time
+ // hasn't changed.
+ shutdownServices();
+
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_5);
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_3, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+
+ initService();
+ mService.handleUnlockUser(USER_0);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2", "ms3", "ms4", "ms5");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ // Do it again, but this time we change the app version, so we do detect the changes.
+ shutdownServices();
+
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ updatePackageLastUpdateTime(CALLING_PACKAGE_3, 1);
+
+ initService();
+ mService.handleUnlockUser(USER_0);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2", "ms3", "ms4", "ms5");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ // Next, try removing all shortcuts, with some of them pinned.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms3"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("ms2"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("ms1"), HANDLE_USER_0);
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2", "ms3", "ms4", "ms5");
+ assertShortcutIds(assertAllImmutable(assertAllPinned(assertAllManifest(
+ assertAllEnabled(mManager.getPinnedShortcuts())))),
+ "ms3");
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2");
+ assertShortcutIds(assertAllImmutable(assertAllPinned(assertAllManifest(
+ assertAllEnabled(mManager.getPinnedShortcuts())))),
+ "ms2");
+ });
+
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+ assertShortcutIds(assertAllImmutable(assertAllPinned(assertAllManifest(
+ assertAllEnabled(mManager.getPinnedShortcuts())))),
+ "ms1");
+ });
+
+ shutdownServices();
+
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_0);
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_3, ShortcutActivity.class.getName()),
+ R.xml.shortcut_0);
+
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ updatePackageVersion(CALLING_PACKAGE_2, 1);
+ updatePackageVersion(CALLING_PACKAGE_3, 1);
+
+ initService();
+ mService.handleUnlockUser(USER_0);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertShortcutIds(assertAllImmutable(assertAllPinned(assertAllNotManifest(
+ assertAllDisabled(mManager.getPinnedShortcuts())))),
+ "ms3");
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+ assertShortcutIds(assertAllImmutable(assertAllPinned(assertAllNotManifest(
+ assertAllDisabled(mManager.getPinnedShortcuts())))),
+ "ms2");
+ });
+
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertShortcutIds(assertAllImmutable(assertAllPinned(assertAllNotManifest(
+ assertAllDisabled(mManager.getPinnedShortcuts())))),
+ "ms1");
+ });
+
+ // Make sure we don't have ShortcutPackage for packages that don't have shortcuts.
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_4, USER_0));
+ assertNull(mService.getPackageShortcutForTest(LAUNCHER_1, USER_0));
+ }
+
+ public void testManifestShortcut_publishOnBroadcast() {
+ // First, no packages are installed.
+ uninstallPackage(USER_0, CALLING_PACKAGE_1);
+ uninstallPackage(USER_0, CALLING_PACKAGE_2);
+ uninstallPackage(USER_0, CALLING_PACKAGE_3);
+ uninstallPackage(USER_0, CALLING_PACKAGE_4);
+ uninstallPackage(USER_10, CALLING_PACKAGE_1);
+ uninstallPackage(USER_10, CALLING_PACKAGE_2);
+ uninstallPackage(USER_10, CALLING_PACKAGE_3);
+ uninstallPackage(USER_10, CALLING_PACKAGE_4);
+
+ mService.handleUnlockUser(USER_0);
+
+ mRunningUsers.put(USER_10, true);
+ mService.handleUnlockUser(USER_10);
+
+ // Originally no manifest shortcuts.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ // Package 1 updated, with manifest shortcuts.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ // Package 2 updated, with manifest shortcuts.
+
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()),
+ R.xml.shortcut_5);
+ updatePackageVersion(CALLING_PACKAGE_2, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_2, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2", "ms3", "ms4", "ms5");
+ assertWith(getCallerShortcuts()).selectManifest()
+ .selectByActivity(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()))
+ .haveRanksInOrder("ms1", "ms2", "ms3", "ms4", "ms5");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ // Package 2 updated, with less manifest shortcuts.
+ // This time we use updatePackageLastUpdateTime() instead of updatePackageVersion().
+
+ dumpsysOnLogcat("Before pinning");
+
+ // Also pin some.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("ms2", "ms3"), HANDLE_USER_0);
+ });
+
+ dumpsysOnLogcat("After pinning");
+
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ updatePackageLastUpdateTime(CALLING_PACKAGE_2, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_2, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2");
+ assertWith(getCallerShortcuts()).selectManifest()
+ .selectByActivity(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()))
+ .haveRanksInOrder("ms1", "ms2");
+ assertShortcutIds(assertAllImmutable(assertAllPinned(
+ mManager.getPinnedShortcuts())),
+ "ms2", "ms3");
+ // ms3 is no longer in manifest, so should be disabled.
+ // but ms1 and ms2 should be enabled.
+ assertAllEnabled(list(getCallerShortcut("ms1")));
+ assertAllEnabled(list(getCallerShortcut("ms2")));
+ assertAllDisabled(list(getCallerShortcut("ms3")));
+ });
+
+ // Package 2 on user 10 has no shortcuts yet.
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+ // Send add broadcast, but the user is not running, so should be ignored.
+ mService.handleCleanupUser(USER_10);
+ mRunningUsers.put(USER_10, false);
+ mUnlockedUsers.put(USER_10, false);
+
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_2, USER_10));
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ // Don't use the mManager APIs to get shortcuts, because they'll trigger the package
+ // update check.
+ // So look the internal data directly using getCallerShortcuts().
+ assertEmpty(getCallerShortcuts());
+ });
+
+ // Try again, but the user is locked, so still ignored.
+ mRunningUsers.put(USER_10, true);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_2, USER_10));
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ // Don't use the mManager APIs to get shortcuts, because they'll trigger the package
+ // update check.
+ // So look the internal data directly using getCallerShortcuts().
+ assertEmpty(getCallerShortcuts());
+ });
+
+ // Unlock the user, now it should work.
+ mUnlockedUsers.put(USER_10, true);
+
+ // Send PACKAGE_ADD broadcast to have Package 2 on user-10 publish manifest shortcuts.
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_2, USER_10));
+
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2");
+ assertWith(getCallerShortcuts()).selectManifest()
+ .selectByActivity(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()))
+ .haveRanksInOrder("ms1", "ms2");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ // But it shouldn't affect user-0.
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2");
+ assertWith(getCallerShortcuts()).selectManifest()
+ .selectByActivity(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()))
+ .haveRanksInOrder("ms1", "ms2");
+ assertShortcutIds(assertAllImmutable(assertAllPinned(
+ mManager.getPinnedShortcuts())),
+ "ms2", "ms3");
+ assertAllEnabled(list(getCallerShortcut("ms1")));
+ assertAllEnabled(list(getCallerShortcut("ms2")));
+ assertAllDisabled(list(getCallerShortcut("ms3")));
+ });
+
+ // Multiple activities.
+ // Add shortcuts on activity 2 for package 2.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()),
+ R.xml.shortcut_5_alt);
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity2.class.getName()),
+ R.xml.shortcut_5_reverse);
+
+ updatePackageLastUpdateTime(CALLING_PACKAGE_2, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_2, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2", "ms3", "ms4", "ms5",
+ "ms1_alt", "ms2_alt", "ms3_alt", "ms4_alt", "ms5_alt");
+
+ // Make sure they have the correct ranks, regardless of their ID's alphabetical order.
+ assertWith(getCallerShortcuts()).selectManifest()
+ .selectByActivity(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()))
+ .haveRanksInOrder("ms1_alt", "ms2_alt", "ms3_alt", "ms4_alt", "ms5_alt");
+ assertWith(getCallerShortcuts()).selectManifest()
+ .selectByActivity(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity2.class.getName()))
+ .haveRanksInOrder("ms5", "ms4", "ms3", "ms2", "ms1");
+ });
+
+ // Package 2 now has no manifest shortcuts.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()),
+ R.xml.shortcut_0);
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity2.class.getName()),
+ R.xml.shortcut_0);
+ updatePackageLastUpdateTime(CALLING_PACKAGE_2, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_2, USER_0));
+
+ // No manifest shortcuts, and pinned ones are disabled.
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertShortcutIds(assertAllImmutable(assertAllPinned(assertAllDisabled(
+ mManager.getPinnedShortcuts()))),
+ "ms2", "ms3");
+ });
+ }
+
+ public void testManifestShortcuts_missingMandatoryFields() {
+ // Start with no apps installed.
+ uninstallPackage(USER_0, CALLING_PACKAGE_1);
+ uninstallPackage(USER_0, CALLING_PACKAGE_2);
+ uninstallPackage(USER_0, CALLING_PACKAGE_3);
+ uninstallPackage(USER_0, CALLING_PACKAGE_4);
+
+ mService.handleUnlockUser(USER_0);
+
+ // Make sure no manifest shortcuts.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ });
+
+ // Package 1 updated, which has one valid manifest shortcut and one invalid.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_error_1);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // Only the valid one is published.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .areAllManifest()
+ .areAllImmutable()
+ .areAllEnabled()
+ .haveIds("x1");
+ });
+
+ // Package 1 updated, which has one valid manifest shortcut and one invalid.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_error_2);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // Only the valid one is published.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .areAllManifest()
+ .areAllImmutable()
+ .areAllEnabled()
+ .haveIds("x2");
+ });
+
+ // Package 1 updated, which has one valid manifest shortcut and one invalid.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_error_3);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // Only the valid one is published.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .areAllManifest()
+ .areAllImmutable()
+ .areAllEnabled()
+ .haveIds("x3")
+ .forShortcutWithId("x3", si -> {
+ assertEquals(set("cat2"), si.getCategories());
+ });
+ });
+ }
+
+ public void testManifestShortcuts_intentDefinitions() {
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_error_4);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ // Make sure invalid ones are not published.
+ // Note that at this point disabled ones don't show up because they weren't pinned.
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1", "ms2")
+ .areAllManifest()
+ .areAllNotDynamic()
+ .areAllNotPinned()
+ .areAllImmutable()
+ .areAllEnabled()
+ .forShortcutWithId("ms1", si -> {
+ assertTrue(si.isEnabled());
+ assertEquals(1, si.getIntents().length);
+
+ assertEquals("action1", si.getIntent().getAction());
+ assertEquals("value1", si.getIntent().getStringExtra("key1"));
+ assertEquals(Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_CLEAR_TASK |
+ Intent.FLAG_ACTIVITY_TASK_ON_HOME, si.getIntent().getFlags());
+
+ assertEquals("action1", si.getIntents()[0].getAction());
+ assertEquals("value1", si.getIntents()[0].getStringExtra("key1"));
+ assertEquals(Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_CLEAR_TASK |
+ Intent.FLAG_ACTIVITY_TASK_ON_HOME, si.getIntents()[0].getFlags());
+ })
+ .forShortcutWithId("ms2", si -> {
+ assertTrue(si.isEnabled());
+ assertEquals(2, si.getIntents().length);
+
+ // getIntent will return the last one.
+ assertEquals("action2_2", si.getIntent().getAction());
+ assertEquals("value2", si.getIntent().getStringExtra("key2"));
+ assertEquals(0, si.getIntent().getFlags());
+
+ final Intent i1 = si.getIntents()[0];
+ final Intent i2 = si.getIntents()[1];
+
+ assertEquals("action2_1", i1.getAction());
+ assertEquals("value1", i1.getStringExtra("key1"));
+ assertEquals(Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_CLEAR_TASK |
+ Intent.FLAG_ACTIVITY_TASK_ON_HOME, i1.getFlags());
+
+ assertEquals("action2_2", i2.getAction());
+ assertEquals("value2", i2.getStringExtra("key2"));
+ assertEquals(0, i2.getFlags());
+ });
+ });
+
+ // Publish 5 enabled to pin some, so we can later test disabled manfiest shortcuts..
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_5);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ // Make sure 5 manifest shortcuts are published.
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1", "ms2", "ms3", "ms4", "ms5")
+ .areAllManifest()
+ .areAllNotDynamic()
+ .areAllNotPinned()
+ .areAllImmutable()
+ .areAllEnabled();
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("ms3", "ms4", "ms5"), HANDLE_USER_0);
+ });
+
+ // Make sure they're pinned.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1", "ms2", "ms3", "ms4", "ms5")
+ .selectByIds("ms1", "ms2")
+ .areAllNotPinned()
+ .areAllEnabled()
+
+ .revertToOriginalList()
+ .selectByIds("ms3", "ms4", "ms5")
+ .areAllPinned()
+ .areAllEnabled();
+ });
+
+ // Update the app.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_error_4);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // Make sure 3, 4 and 5 still exist but disabled.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1", "ms2", "ms3", "ms4", "ms5")
+ .areAllNotDynamic()
+ .areAllImmutable()
+
+ .selectByIds("ms1", "ms2")
+ .areAllManifest()
+ .areAllNotPinned()
+ .areAllEnabled()
+
+ .revertToOriginalList()
+ .selectByIds("ms3", "ms4", "ms5")
+ .areAllNotManifest()
+ .areAllPinned()
+ .areAllDisabled()
+
+ .revertToOriginalList()
+ .forShortcutWithId("ms1", si -> {
+ assertEquals(si.getId(), "action1", si.getIntent().getAction());
+ })
+ .forShortcutWithId("ms2", si -> {
+ // getIntent returns the last one.
+ assertEquals(si.getId(), "action2_2", si.getIntent().getAction());
+ })
+ .forShortcutWithId("ms3", si -> {
+ assertEquals(si.getId(), Intent.ACTION_VIEW, si.getIntent().getAction());
+ })
+ .forShortcutWithId("ms4", si -> {
+ assertEquals(si.getId(), Intent.ACTION_VIEW, si.getIntent().getAction());
+ })
+ .forShortcutWithId("ms5", si -> {
+ assertEquals(si.getId(), "action", si.getIntent().getAction());
+ });
+ });
+ }
+
+ public void testManifestShortcuts_checkAllFields() {
+ mService.handleUnlockUser(USER_0);
+
+ // Package 1 updated, which has one valid manifest shortcut and one invalid.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_5);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // Only the valid one is published.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1", "ms2", "ms3", "ms4", "ms5")
+ .areAllManifest()
+ .areAllImmutable()
+ .areAllEnabled()
+ .areAllNotPinned()
+ .areAllNotDynamic()
+
+ .forShortcutWithId("ms1", si -> {
+ assertEquals(R.drawable.icon1, si.getIconResourceId());
+ assertEquals(new ComponentName(CALLING_PACKAGE_1,
+ ShortcutActivity.class.getName()),
+ si.getActivity());
+
+ assertEquals(R.string.shortcut_title1, si.getTitleResId());
+ assertEquals("r" + R.string.shortcut_title1, si.getTitleResName());
+ assertEquals(R.string.shortcut_text1, si.getTextResId());
+ assertEquals("r" + R.string.shortcut_text1, si.getTextResName());
+ assertEquals(R.string.shortcut_disabled_message1,
+ si.getDisabledMessageResourceId());
+ assertEquals("r" + R.string.shortcut_disabled_message1,
+ si.getDisabledMessageResName());
+
+ assertEquals(set("android.shortcut.conversation", "android.shortcut.media"),
+ si.getCategories());
+ assertEquals("action1", si.getIntent().getAction());
+ assertEquals(Uri.parse("http://a.b.c/1"), si.getIntent().getData());
+ })
+
+ .forShortcutWithId("ms2", si -> {
+ assertEquals("ms2", si.getId());
+ assertEquals(R.drawable.icon2, si.getIconResourceId());
+
+ assertEquals(R.string.shortcut_title2, si.getTitleResId());
+ assertEquals("r" + R.string.shortcut_title2, si.getTitleResName());
+ assertEquals(R.string.shortcut_text2, si.getTextResId());
+ assertEquals("r" + R.string.shortcut_text2, si.getTextResName());
+ assertEquals(R.string.shortcut_disabled_message2,
+ si.getDisabledMessageResourceId());
+ assertEquals("r" + R.string.shortcut_disabled_message2,
+ si.getDisabledMessageResName());
+
+ assertEquals(set("android.shortcut.conversation"), si.getCategories());
+ assertEquals("action2", si.getIntent().getAction());
+ assertEquals(null, si.getIntent().getData());
+ })
+
+ .forShortcutWithId("ms3", si -> {
+ assertEquals(0, si.getIconResourceId());
+ assertEquals(R.string.shortcut_title1, si.getTitleResId());
+ assertEquals("r" + R.string.shortcut_title1, si.getTitleResName());
+
+ assertEquals(0, si.getTextResId());
+ assertEquals(null, si.getTextResName());
+ assertEquals(0, si.getDisabledMessageResourceId());
+ assertEquals(null, si.getDisabledMessageResName());
+
+ assertEmpty(si.getCategories());
+ assertEquals("android.intent.action.VIEW", si.getIntent().getAction());
+ assertEquals(null, si.getIntent().getData());
+ })
+
+ .forShortcutWithId("ms4", si -> {
+ assertEquals(0, si.getIconResourceId());
+ assertEquals(R.string.shortcut_title2, si.getTitleResId());
+ assertEquals("r" + R.string.shortcut_title2, si.getTitleResName());
+
+ assertEquals(0, si.getTextResId());
+ assertEquals(null, si.getTextResName());
+ assertEquals(0, si.getDisabledMessageResourceId());
+ assertEquals(null, si.getDisabledMessageResName());
+
+ assertEquals(set("cat"), si.getCategories());
+ assertEquals("android.intent.action.VIEW2", si.getIntent().getAction());
+ assertEquals(null, si.getIntent().getData());
+ })
+
+ .forShortcutWithId("ms5", si -> {
+ si = getCallerShortcut("ms5");
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("http://www/", si.getIntent().getData().toString());
+ assertEquals("foo/bar", si.getIntent().getType());
+ assertEquals(
+ new ComponentName("abc", ".xyz"), si.getIntent().getComponent());
+
+ assertEquals(set("cat1", "cat2"), si.getIntent().getCategories());
+ assertEquals("value1", si.getIntent().getStringExtra("key1"));
+ assertEquals("value2", si.getIntent().getStringExtra("key2"));
+ });
+ });
+ }
+
+ public void testManifestShortcuts_localeChange() throws InterruptedException {
+ mService.handleUnlockUser(USER_0);
+
+ // Package 1 updated, which has one valid manifest shortcut and one invalid.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.setDynamicShortcuts(list(makeShortcutWithTitle("s1", "title")));
+
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2");
+
+ // check first shortcut.
+ ShortcutInfo si = getCallerShortcut("ms1");
+
+ assertEquals("ms1", si.getId());
+ assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_title1 + "/en",
+ si.getTitle());
+ assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_text1 + "/en",
+ si.getText());
+ assertEquals("string-com.android.test.1-user:0-res:"
+ + R.string.shortcut_disabled_message1 + "/en",
+ si.getDisabledMessage());
+ assertEquals(START_TIME, si.getLastChangedTimestamp());
+
+ // check another
+ si = getCallerShortcut("ms2");
+
+ assertEquals("ms2", si.getId());
+ assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_title2 + "/en",
+ si.getTitle());
+ assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_text2 + "/en",
+ si.getText());
+ assertEquals("string-com.android.test.1-user:0-res:"
+ + R.string.shortcut_disabled_message2 + "/en",
+ si.getDisabledMessage());
+ assertEquals(START_TIME, si.getLastChangedTimestamp());
+
+ // Check the dynamic one.
+ si = getCallerShortcut("s1");
+
+ assertEquals("s1", si.getId());
+ assertEquals("title", si.getTitle());
+ assertEquals(null, si.getText());
+ assertEquals(null, si.getDisabledMessage());
+ assertEquals(START_TIME, si.getLastChangedTimestamp());
+ });
+
+ mInjectedCurrentTimeMillis++;
+
+ // Change the locale and send the broadcast, make sure the launcher gets a callback too.
+ mInjectedLocale = Locale.JAPANESE;
+
+ setCaller(LAUNCHER_1, USER_0);
+
+ assertForLauncherCallback(mLauncherApps, () -> {
+ mService.mReceiver.onReceive(mServiceContext, new Intent(Intent.ACTION_LOCALE_CHANGED));
+ }).assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_0)
+ .haveIds("ms1", "ms2", "s1");
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ // check first shortcut.
+ ShortcutInfo si = getCallerShortcut("ms1");
+
+ assertEquals("ms1", si.getId());
+ assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_title1 + "/ja",
+ si.getTitle());
+ assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_text1 + "/ja",
+ si.getText());
+ assertEquals("string-com.android.test.1-user:0-res:"
+ + R.string.shortcut_disabled_message1 + "/ja",
+ si.getDisabledMessage());
+ assertEquals(START_TIME + 1, si.getLastChangedTimestamp());
+
+ // check another
+ si = getCallerShortcut("ms2");
+
+ assertEquals("ms2", si.getId());
+ assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_title2 + "/ja",
+ si.getTitle());
+ assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_text2 + "/ja",
+ si.getText());
+ assertEquals("string-com.android.test.1-user:0-res:"
+ + R.string.shortcut_disabled_message2 + "/ja",
+ si.getDisabledMessage());
+ assertEquals(START_TIME + 1, si.getLastChangedTimestamp());
+
+ // Check the dynamic one. (locale change shouldn't affect.)
+ si = getCallerShortcut("s1");
+
+ assertEquals("s1", si.getId());
+ assertEquals("title", si.getTitle());
+ assertEquals(null, si.getText());
+ assertEquals(null, si.getDisabledMessage());
+ assertEquals(START_TIME, si.getLastChangedTimestamp()); // Not changed.
+ });
+ }
+
+ public void testManifestShortcuts_updateAndDisabled_notPinned() {
+ mService.handleUnlockUser(USER_0);
+
+ // First, just publish a manifest shortcut.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // Only the valid one is published.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+ assertEmpty(mManager.getPinnedShortcuts());
+
+ // Make sure there's no other dangling shortcuts.
+ assertShortcutIds(getCallerShortcuts(), "ms1");
+ });
+
+ // Now version up, the manifest shortcut is disabled now.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1_disable);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // Because shortcut 1 wasn't pinned, it'll just go away.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertEmpty(mManager.getPinnedShortcuts());
+
+ // Make sure there's no other dangling shortcuts.
+ assertEmpty(getCallerShortcuts());
+ });
+ }
+
+ public void testManifestShortcuts_updateAndDisabled_pinned() {
+ mService.handleUnlockUser(USER_0);
+
+ // First, just publish a manifest shortcut.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // Only the valid one is published.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+ assertEmpty(mManager.getPinnedShortcuts());
+
+ // Make sure there's no other dangling shortcuts.
+ assertShortcutIds(getCallerShortcuts(), "ms1");
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms1"), HANDLE_USER_0);
+ });
+
+ // Now upgrade, the manifest shortcut is disabled now.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1_disable);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // Because shortcut 1 was pinned, it'll still exist as pinned, but disabled.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertShortcutIds(assertAllNotManifest(assertAllImmutable(assertAllDisabled(
+ mManager.getPinnedShortcuts()))),
+ "ms1");
+
+ // Make sure the fields are updated.
+ ShortcutInfo si = getCallerShortcut("ms1");
+
+ assertEquals("ms1", si.getId());
+ assertEquals(R.drawable.icon2, si.getIconResourceId());
+ assertEquals(R.string.shortcut_title2, si.getTitleResId());
+ assertEquals(R.string.shortcut_text2, si.getTextResId());
+ assertEquals(R.string.shortcut_disabled_message2, si.getDisabledMessageResourceId());
+ assertEquals(Intent.ACTION_VIEW, si.getIntent().getAction());
+
+ // Make sure there's no other dangling shortcuts.
+ assertShortcutIds(getCallerShortcuts(), "ms1");
+ });
+ }
+
+ public void testManifestShortcuts_duplicateInSingleActivity() {
+ mService.handleUnlockUser(USER_0);
+
+ // The XML has two shortcuts with the same ID.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2_duplicate);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+
+ // Make sure the first one has survived. (the second one has a different title.)
+ ShortcutInfo si = getCallerShortcut("ms1");
+ assertEquals(R.string.shortcut_title1, si.getTitleResId());
+
+ // Make sure there's no other dangling shortcuts.
+ assertShortcutIds(getCallerShortcuts(), "ms1");
+ });
+ }
+
+ public void testManifestShortcuts_duplicateInTwoActivities() {
+ mService.handleUnlockUser(USER_0);
+
+ // ShortcutActivity has shortcut ms1
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+
+ // ShortcutActivity2 has two shortcuts, ms1 and ms2.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity2.class.getName()),
+ R.xml.shortcut_5);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2", "ms3", "ms4", "ms5");
+
+ // ms1 should belong to ShortcutActivity.
+ ShortcutInfo si = getCallerShortcut("ms1");
+ assertEquals(R.string.shortcut_title1, si.getTitleResId());
+ assertEquals(new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ si.getActivity());
+ assertEquals(0, si.getRank());
+
+ // ms2 should belong to ShortcutActivity*2*.
+ si = getCallerShortcut("ms2");
+ assertEquals(R.string.shortcut_title2, si.getTitleResId());
+ assertEquals(new ComponentName(CALLING_PACKAGE_1, ShortcutActivity2.class.getName()),
+ si.getActivity());
+
+ // Also check the ranks
+ assertWith(getCallerShortcuts()).selectManifest()
+ .selectByActivity(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()))
+ .haveRanksInOrder("ms1");
+ assertWith(getCallerShortcuts()).selectManifest()
+ .selectByActivity(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity2.class.getName()))
+ .haveRanksInOrder("ms2", "ms3", "ms4", "ms5");
+
+ // Make sure there's no other dangling shortcuts.
+ assertShortcutIds(getCallerShortcuts(), "ms1", "ms2", "ms3", "ms4", "ms5");
+ });
+ }
+
+ /**
+ * Manifest shortcuts cannot override shortcuts that were published via the APIs.
+ */
+ public void testManifestShortcuts_cannotOverrideNonManifest() {
+ mService.handleUnlockUser(USER_0);
+
+ // Create a non-pinned dynamic shortcut and a non-dynamic pinned shortcut.
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.setDynamicShortcuts(list(
+ makeShortcut("ms1", "title1",
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ /* icon */ null, new Intent("action1"), /* rank */ 0),
+ makeShortcut("ms2", "title2",
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ /* icon */ null, new Intent("action1"), /* rank */ 0)));
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms2"), HANDLE_USER_0);
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.removeDynamicShortcuts(list("ms2"));
+
+ assertShortcutIds(mManager.getDynamicShortcuts(), "ms1");
+ assertShortcutIds(mManager.getPinnedShortcuts(), "ms2");
+ assertEmpty(mManager.getManifestShortcuts());
+ });
+
+ // Then update the app with 5 manifest shortcuts.
+ // Make sure "ms1" and "ms2" won't be replaced.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_5);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllNotManifest(mManager.getDynamicShortcuts()), "ms1");
+ assertShortcutIds(assertAllNotManifest(mManager.getPinnedShortcuts()), "ms2");
+ assertShortcutIds(assertAllManifest(mManager.getManifestShortcuts()),
+ "ms3", "ms4", "ms5");
+
+ // ms1 and ms2 shouold keep the original title.
+ ShortcutInfo si = getCallerShortcut("ms1");
+ assertEquals("title1", si.getTitle());
+
+ si = getCallerShortcut("ms2");
+ assertEquals("title2", si.getTitle());
+ });
+ }
+
+ protected void checkManifestShortcuts_immutable_verify() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllNotManifest(assertAllEnabled(
+ mManager.getDynamicShortcuts())),
+ "s1");
+ assertShortcutIds(assertAllManifest(assertAllEnabled(
+ mManager.getManifestShortcuts())),
+ "ms1");
+ assertShortcutIds(assertAllNotManifest(assertAllDisabled(
+ mManager.getPinnedShortcuts())),
+ "ms2");
+
+ assertEquals("t1", getCallerShortcut("s1").getTitle());
+
+ // Make sure there are no other shortcuts.
+ assertShortcutIds(getCallerShortcuts(), "s1", "ms1", "ms2");
+ });
+ }
+
+ /**
+ * Make sure the APIs won't work on manifest shortcuts.
+ */
+ public void testManifestShortcuts_immutable() {
+ mService.handleUnlockUser(USER_0);
+
+ // Create a non-pinned manifest shortcut, a pinned shortcut that was originally
+ // a manifest shortcut, as well as a dynamic shortcut.
+
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms2"), HANDLE_USER_0);
+ });
+
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.addDynamicShortcuts(list(makeShortcutWithTitle("s1", "t1")));
+ });
+
+ checkManifestShortcuts_immutable_verify();
+
+ // Note that even though the first argument is not immutable and only the second one
+ // is immutable, the first argument should not be executed either.
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertCannotUpdateImmutable(() -> {
+ mManager.setDynamicShortcuts(list(makeShortcut("xx"), makeShortcut("ms1")));
+ });
+ assertCannotUpdateImmutable(() -> {
+ mManager.setDynamicShortcuts(list(makeShortcut("xx"), makeShortcut("ms2")));
+ });
+ });
+ checkManifestShortcuts_immutable_verify();
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertCannotUpdateImmutable(() -> {
+ mManager.addDynamicShortcuts(list(makeShortcut("xx"), makeShortcut("ms1")));
+ });
+ assertCannotUpdateImmutable(() -> {
+ mManager.addDynamicShortcuts(list(makeShortcut("xx"), makeShortcut("ms2")));
+ });
+ });
+ checkManifestShortcuts_immutable_verify();
+
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertCannotUpdateImmutable(() -> {
+ mManager.updateShortcuts(list(makeShortcut("s1"), makeShortcut("ms1")));
+ });
+ assertCannotUpdateImmutable(() -> {
+ mManager.updateShortcuts(list(makeShortcut("s1"), makeShortcut("ms2")));
+ });
+ });
+ checkManifestShortcuts_immutable_verify();
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertCannotUpdateImmutable(() -> {
+ mManager.removeDynamicShortcuts(list("s1", "ms1"));
+ });
+ assertCannotUpdateImmutable(() -> {
+ mManager.removeDynamicShortcuts(list("s2", "ms2"));
+ });
+ });
+ checkManifestShortcuts_immutable_verify();
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertCannotUpdateImmutable(() -> {
+ mManager.disableShortcuts(list("s1", "ms1"));
+ });
+ });
+ checkManifestShortcuts_immutable_verify();
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertCannotUpdateImmutable(() -> {
+ mManager.enableShortcuts(list("s1", "ms2"));
+ });
+ });
+ checkManifestShortcuts_immutable_verify();
+ }
+
+
+ /**
+ * Make sure the APIs won't work on manifest shortcuts.
+ */
+ public void testManifestShortcuts_tooMany() {
+ // Change the max number of shortcuts.
+ mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
+
+ mService.handleUnlockUser(USER_0);
+
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_5);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ // Only the first 3 should be published.
+ assertShortcutIds(mManager.getManifestShortcuts(), "ms1", "ms2", "ms3");
+ });
+ }
+
+ public void testMaxShortcutCount_set() {
+ // Change the max number of shortcuts.
+ mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ final ComponentName a1 = new ComponentName(mClientContext, ShortcutActivity.class);
+ final ComponentName a2 = new ComponentName(mClientContext, ShortcutActivity2.class);
+ final ShortcutInfo s1_1 = makeShortcutWithActivity("s11", a1);
+ final ShortcutInfo s1_2 = makeShortcutWithActivity("s12", a1);
+ final ShortcutInfo s1_3 = makeShortcutWithActivity("s13", a1);
+ final ShortcutInfo s1_4 = makeShortcutWithActivity("s14", a1);
+ final ShortcutInfo s1_5 = makeShortcutWithActivity("s15", a1);
+ final ShortcutInfo s1_6 = makeShortcutWithActivity("s16", a1);
+ final ShortcutInfo s2_1 = makeShortcutWithActivity("s21", a2);
+ final ShortcutInfo s2_2 = makeShortcutWithActivity("s22", a2);
+ final ShortcutInfo s2_3 = makeShortcutWithActivity("s23", a2);
+ final ShortcutInfo s2_4 = makeShortcutWithActivity("s24", a2);
+
+ // 3 shortcuts for 2 activities -> okay
+ mManager.setDynamicShortcuts(list(s1_1, s1_2, s1_3, s2_1, s2_2, s2_3));
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s11", "s12", "s13", "s21", "s22", "s23");
+
+ mManager.removeAllDynamicShortcuts();
+
+ // 4 shortcut for activity 1 -> too many.
+ assertDynamicShortcutCountExceeded(() -> {
+ mManager.setDynamicShortcuts(list(s1_1, s1_2, s1_3, s1_4, s2_1, s2_2, s2_3));
+ });
+ assertEmpty(mManager.getDynamicShortcuts());
+
+ // 4 shortcut for activity 2 -> too many.
+ assertDynamicShortcutCountExceeded(() -> {
+ mManager.setDynamicShortcuts(list(s1_1, s1_2, s1_3, s2_1, s2_2, s2_3, s2_4));
+ });
+ assertEmpty(mManager.getDynamicShortcuts());
+
+ // First, set 3. Then set 4, which should be ignored.
+ mManager.setDynamicShortcuts(list(s1_1, s1_2, s1_3));
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s11", "s12", "s13");
+ assertDynamicShortcutCountExceeded(() -> {
+ mManager.setDynamicShortcuts(list(s2_1, s2_2, s2_3, s2_4));
+ });
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s11", "s12", "s13");
+
+ // Set will remove the old dynamic set, unlike add, so the following should pass.
+ mManager.setDynamicShortcuts(list(s1_1, s1_2, s1_3));
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s11", "s12", "s13");
+ mManager.setDynamicShortcuts(list(s1_4, s1_5, s1_6));
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s14", "s15", "s16");
+
+ // Now, test with 2 manifest shortcuts.
+ mManager.removeAllDynamicShortcuts();
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+ assertEquals(2, mManager.getManifestShortcuts().size());
+
+ // Setting 1 to activity 1 will work.
+ mManager.setDynamicShortcuts(list(s1_1, s2_1, s2_2, s2_3));
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s11", "s21", "s22", "s23");
+ assertEquals(2, mManager.getManifestShortcuts().size());
+
+ // But setting 2 will not.
+ mManager.removeAllDynamicShortcuts();
+ assertDynamicShortcutCountExceeded(() -> {
+ mManager.setDynamicShortcuts(list(s1_1, s1_2, s2_1, s2_2, s2_3));
+ });
+ assertEmpty(mManager.getDynamicShortcuts());
+ assertEquals(2, mManager.getManifestShortcuts().size());
+ });
+ }
+
+ public void testMaxShortcutCount_add() {
+ // Change the max number of shortcuts.
+ mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ final ComponentName a1 = new ComponentName(mClientContext, ShortcutActivity.class);
+ final ComponentName a2 = new ComponentName(mClientContext, ShortcutActivity2.class);
+ final ShortcutInfo s1_1 = makeShortcutWithActivity("s11", a1);
+ final ShortcutInfo s1_2 = makeShortcutWithActivity("s12", a1);
+ final ShortcutInfo s1_3 = makeShortcutWithActivity("s13", a1);
+ final ShortcutInfo s1_4 = makeShortcutWithActivity("s14", a1);
+ final ShortcutInfo s2_1 = makeShortcutWithActivity("s21", a2);
+ final ShortcutInfo s2_2 = makeShortcutWithActivity("s22", a2);
+ final ShortcutInfo s2_3 = makeShortcutWithActivity("s23", a2);
+ final ShortcutInfo s2_4 = makeShortcutWithActivity("s24", a2);
+
+ // 3 shortcuts for 2 activities -> okay
+ mManager.addDynamicShortcuts(list(s1_1, s1_2, s1_3, s2_1, s2_2, s2_3));
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s11", "s12", "s13", "s21", "s22", "s23");
+
+ mManager.removeAllDynamicShortcuts();
+
+ // 4 shortcut for activity 1 -> too many.
+ assertDynamicShortcutCountExceeded(() -> {
+ mManager.addDynamicShortcuts(list(s1_1, s1_2, s1_3, s1_4, s2_1, s2_2, s2_3));
+ });
+ assertEmpty(mManager.getDynamicShortcuts());
+
+ // 4 shortcut for activity 2 -> too many.
+ assertDynamicShortcutCountExceeded(() -> {
+ mManager.addDynamicShortcuts(list(s1_1, s1_2, s1_3, s2_1, s2_2, s2_3, s2_4));
+ });
+ assertEmpty(mManager.getDynamicShortcuts());
+
+ // First, set 3. Then add 1 more, which should be ignored.
+ mManager.setDynamicShortcuts(list(s1_1, s1_2, s1_3));
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s11", "s12", "s13");
+ assertDynamicShortcutCountExceeded(() -> {
+ mManager.addDynamicShortcuts(list(s1_4, s2_1));
+ });
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s11", "s12", "s13");
+
+ // Update existing one, which should work.
+ mManager.addDynamicShortcuts(list(makeShortcutWithActivityAndTitle(
+ "s11", a1, "xxx"), s2_1));
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s11", "s12", "s13", "s21");
+ assertEquals("xxx", getCallerShortcut("s11").getTitle());
+
+ // Make sure pinned shortcuts won't affect.
+ // - Pin s11 - s13, and remove all dynamic.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s11", "s12", "s13"),
+ HANDLE_USER_0);
+ });
+ mManager.removeAllDynamicShortcuts();
+
+ assertEmpty(mManager.getDynamicShortcuts());
+ assertShortcutIds(mManager.getPinnedShortcuts(),
+ "s11", "s12", "s13");
+
+ // Then add dynamic.
+ mManager.addDynamicShortcuts(list(s1_4, s2_1, s2_2, s2_3));
+
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s14", "s21", "s22", "s23");
+ assertShortcutIds(mManager.getPinnedShortcuts(),
+ "s11", "s12", "s13");
+
+ // Adding "s11" and "s12" back, should work
+ mManager.addDynamicShortcuts(list(s1_1, s1_2));
+
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s14", "s11", "s12", "s21", "s22", "s23");
+ assertShortcutIds(mManager.getPinnedShortcuts(),
+ "s11", "s12", "s13");
+
+ // Adding back s13 doesn't work.
+ assertDynamicShortcutCountExceeded(() -> {
+ mManager.addDynamicShortcuts(list(s1_3));
+ });
+
+ assertShortcutIds(filterByActivity(mManager.getDynamicShortcuts(), a1),
+ "s11", "s12", "s14");
+ assertShortcutIds(filterByActivity(mManager.getDynamicShortcuts(), a2),
+ "s21", "s22", "s23");
+
+ // Now swap the activities.
+ mManager.updateShortcuts(list(
+ makeShortcutWithActivity("s11", a2),
+ makeShortcutWithActivity("s21", a1)));
+
+ assertShortcutIds(filterByActivity(mManager.getDynamicShortcuts(), a1),
+ "s21", "s12", "s14");
+ assertShortcutIds(filterByActivity(mManager.getDynamicShortcuts(), a2),
+ "s11", "s22", "s23");
+
+ // Now, test with 2 manifest shortcuts.
+ mManager.removeAllDynamicShortcuts();
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ assertEquals(2, mManager.getManifestShortcuts().size());
+
+ // Adding one shortcut to activity 1 works fine.
+ mManager.addDynamicShortcuts(list(s1_1, s2_1, s2_2, s2_3));
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s11", "s21", "s22", "s23");
+ assertEquals(2, mManager.getManifestShortcuts().size());
+
+ // But adding one more doesn't.
+ assertDynamicShortcutCountExceeded(() -> {
+ mManager.addDynamicShortcuts(list(s1_4, s2_1));
+ });
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s11", "s21", "s22", "s23");
+ assertEquals(2, mManager.getManifestShortcuts().size());
+ });
+ }
+
+ public void testMaxShortcutCount_update() {
+ // Change the max number of shortcuts.
+ mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ final ComponentName a1 = new ComponentName(mClientContext, ShortcutActivity.class);
+ final ComponentName a2 = new ComponentName(mClientContext, ShortcutActivity2.class);
+ final ShortcutInfo s1_1 = makeShortcutWithActivity("s11", a1);
+ final ShortcutInfo s1_2 = makeShortcutWithActivity("s12", a1);
+ final ShortcutInfo s1_3 = makeShortcutWithActivity("s13", a1);
+ final ShortcutInfo s1_4 = makeShortcutWithActivity("s14", a1);
+ final ShortcutInfo s1_5 = makeShortcutWithActivity("s15", a1);
+ final ShortcutInfo s2_1 = makeShortcutWithActivity("s21", a2);
+ final ShortcutInfo s2_2 = makeShortcutWithActivity("s22", a2);
+ final ShortcutInfo s2_3 = makeShortcutWithActivity("s23", a2);
+ final ShortcutInfo s2_4 = makeShortcutWithActivity("s24", a2);
+
+ // 3 shortcuts for 2 activities -> okay
+ mManager.setDynamicShortcuts(list(s1_1, s1_2, s1_3, s2_1, s2_2, s2_3));
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s11", "s12", "s13", "s21", "s22", "s23");
+
+ // Trying to move s11 from a1 to a2 should fail.
+ assertDynamicShortcutCountExceeded(() -> {
+ mManager.updateShortcuts(list(makeShortcutWithActivity("s11", a2)));
+ });
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s11", "s12", "s13", "s21", "s22", "s23");
+
+ // Trying to move s21 from a2 to a1 should also fail.
+ assertDynamicShortcutCountExceeded(() -> {
+ mManager.updateShortcuts(list(makeShortcutWithActivity("s21", a1)));
+ });
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s11", "s12", "s13", "s21", "s22", "s23");
+
+ // But, if we do these two at the same time, it should work.
+ mManager.updateShortcuts(list(
+ makeShortcutWithActivity("s11", a2),
+ makeShortcutWithActivity("s21", a1)));
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s11", "s12", "s13", "s21", "s22", "s23");
+ assertShortcutIds(filterByActivity(mManager.getDynamicShortcuts(), a1),
+ "s21", "s12", "s13");
+ assertShortcutIds(filterByActivity(mManager.getDynamicShortcuts(), a2),
+ "s11", "s22", "s23");
+
+ // Then reset.
+ mManager.setDynamicShortcuts(list(s1_1, s1_2, s1_3, s2_1, s2_2, s2_3));
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s11", "s12", "s13", "s21", "s22", "s23");
+
+ // Pin some to have more shortcuts for a1.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s11", "s12", "s13"),
+ HANDLE_USER_0);
+ });
+ mManager.setDynamicShortcuts(list(s1_4, s1_5, s2_1, s2_2, s2_3));
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s14", "s15", "s21", "s22", "s23");
+ assertShortcutIds(mManager.getPinnedShortcuts(),
+ "s11", "s12", "s13");
+
+ // a1 already has 2 dynamic shortcuts (and 3 pinned shortcuts that used to belong on it)
+ // But that doesn't matter for update -- the following should still work.
+ mManager.updateShortcuts(list(
+ makeShortcutWithActivityAndTitle("s11", a1, "xxx1"),
+ makeShortcutWithActivityAndTitle("s12", a1, "xxx2"),
+ makeShortcutWithActivityAndTitle("s13", a1, "xxx3"),
+ makeShortcutWithActivityAndTitle("s14", a1, "xxx4"),
+ makeShortcutWithActivityAndTitle("s15", a1, "xxx5")));
+ // All the shortcuts should still exist they all belong on same activities,
+ // with the updated titles.
+ assertShortcutIds(mManager.getDynamicShortcuts(),
+ "s14", "s15", "s21", "s22", "s23");
+ assertShortcutIds(mManager.getPinnedShortcuts(),
+ "s11", "s12", "s13");
+
+ assertShortcutIds(filterByActivity(mManager.getDynamicShortcuts(), a1),
+ "s14", "s15");
+ assertShortcutIds(filterByActivity(mManager.getDynamicShortcuts(), a2),
+ "s21", "s22", "s23");
+
+ assertEquals("xxx1", getCallerShortcut("s11").getTitle());
+ assertEquals("xxx2", getCallerShortcut("s12").getTitle());
+ assertEquals("xxx3", getCallerShortcut("s13").getTitle());
+ assertEquals("xxx4", getCallerShortcut("s14").getTitle());
+ assertEquals("xxx5", getCallerShortcut("s15").getTitle());
+ });
+ }
+
+ public void testShortcutsPushedOutByManifest() {
+ // Change the max number of shortcuts.
+ mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ final ComponentName a1 = new ComponentName(mClientContext, ShortcutActivity.class);
+ final ComponentName a2 = new ComponentName(mClientContext, ShortcutActivity2.class);
+ final ShortcutInfo s1_1 = makeShortcutWithActivityAndRank("s11", a1, 4);
+ final ShortcutInfo s1_2 = makeShortcutWithActivityAndRank("s12", a1, 3);
+ final ShortcutInfo s1_3 = makeShortcutWithActivityAndRank("s13", a1, 2);
+ final ShortcutInfo s1_4 = makeShortcutWithActivityAndRank("s14", a1, 1);
+ final ShortcutInfo s1_5 = makeShortcutWithActivityAndRank("s15", a1, 0);
+ final ShortcutInfo s2_1 = makeShortcutWithActivityAndRank("s21", a2, 0);
+ final ShortcutInfo s2_2 = makeShortcutWithActivityAndRank("s22", a2, 1);
+ final ShortcutInfo s2_3 = makeShortcutWithActivityAndRank("s23", a2, 2);
+ final ShortcutInfo s2_4 = makeShortcutWithActivityAndRank("s24", a2, 3);
+ final ShortcutInfo s2_5 = makeShortcutWithActivityAndRank("s25", a2, 4);
+
+ // Initial state.
+ mManager.setDynamicShortcuts(list(s1_1, s1_2, s1_3, s2_1, s2_2, s2_3));
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s11", "s12", "s21", "s22"),
+ HANDLE_USER_0);
+ });
+ mManager.setDynamicShortcuts(list(s1_2, s1_3, s1_4, s2_2, s2_3, s2_4));
+ assertShortcutIds(assertAllEnabled(mManager.getDynamicShortcuts()),
+ "s12", "s13", "s14",
+ "s22", "s23", "s24");
+ assertShortcutIds(assertAllEnabled(mManager.getPinnedShortcuts()),
+ "s11", "s12",
+ "s21", "s22");
+
+ // Add 1 manifest shortcut to a1.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+ assertEquals(1, mManager.getManifestShortcuts().size());
+
+ // s12 removed.
+ assertShortcutIds(assertAllEnabled(mManager.getDynamicShortcuts()),
+ "s13", "s14",
+ "s22", "s23", "s24");
+ assertShortcutIds(assertAllEnabled(mManager.getPinnedShortcuts()),
+ "s11", "s12",
+ "s21", "s22");
+
+ // Add more manifest shortcuts.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity2.class.getName()),
+ R.xml.shortcut_1_alt);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+ assertEquals(3, mManager.getManifestShortcuts().size());
+
+ // Note the ones with the highest rank values (== least important) will be removed.
+ assertShortcutIds(assertAllEnabled(mManager.getDynamicShortcuts()),
+ "s14",
+ "s22", "s23");
+ assertShortcutIds(assertAllEnabled(mManager.getPinnedShortcuts()),
+ "s11", "s12",
+ "s21", "s22");
+
+ // Add more manifest shortcuts.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity2.class.getName()),
+ R.xml.shortcut_5_alt); // manifest has 5, but max is 3, so a2 will have 3.
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+ assertEquals(5, mManager.getManifestShortcuts().size());
+
+ assertShortcutIds(assertAllEnabled(mManager.getDynamicShortcuts()),
+ "s14" // a1 has 1 dynamic
+ ); // a2 has no dynamic
+ assertShortcutIds(assertAllEnabled(mManager.getPinnedShortcuts()),
+ "s11", "s12",
+ "s21", "s22");
+
+ // Update, no manifest shortucts. This doesn't affect anything.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_0);
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity2.class.getName()),
+ R.xml.shortcut_0);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+ assertEquals(0, mManager.getManifestShortcuts().size());
+
+ assertShortcutIds(assertAllEnabled(mManager.getDynamicShortcuts()),
+ "s14");
+ assertShortcutIds(assertAllEnabled(mManager.getPinnedShortcuts()),
+ "s11", "s12",
+ "s21", "s22");
+ });
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
new file mode 100644
index 0000000..d25923c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -0,0 +1,2040 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertBundlesEqual;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.makeBundle;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.parceled;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.Manifest.permission;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.content.res.Resources;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.PersistableBundle;
+import android.os.UserHandle;
+import android.test.MoreAsserts;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.frameworks.servicestests.R;
+import com.android.server.pm.ShortcutService.ConfigConstants;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Locale;
+
+/**
+ * Tests for ShortcutService and ShortcutManager.
+ *
+ m FrameworksServicesTests &&
+ adb install \
+ -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+ adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest2 \
+ -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ */
+@SmallTest
+public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
+ // ShortcutInfo tests
+
+ public void testShortcutInfoMissingMandatoryFields() {
+ // Disable throttling.
+ mService.updateConfigurationLocked(
+ ShortcutService.ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL + "=99999999,"
+ + ShortcutService.ConfigConstants.KEY_MAX_SHORTCUTS + "=99999999"
+ );
+
+ assertExpectException(
+ IllegalArgumentException.class,
+ "ID must be provided",
+ () -> new ShortcutInfo.Builder(getTestContext()).build());
+
+ assertExpectException(
+ RuntimeException.class,
+ "id cannot be empty",
+ () -> new ShortcutInfo.Builder(getTestContext(), null));
+
+ assertExpectException(
+ RuntimeException.class,
+ "id cannot be empty",
+ () -> new ShortcutInfo.Builder(getTestContext(), ""));
+
+ assertExpectException(
+ RuntimeException.class,
+ "intents cannot contain null",
+ () -> new ShortcutInfo.Builder(getTestContext(), "id").setIntent(null));
+
+ assertExpectException(
+ RuntimeException.class,
+ "action must be set",
+ () -> new ShortcutInfo.Builder(getTestContext(), "id").setIntent(new Intent()));
+
+ assertExpectException(
+ RuntimeException.class,
+ "action must be set",
+ () -> new ShortcutInfo.Builder(getTestContext(), "id")
+ .setIntents(new Intent[]{new Intent("action"), new Intent()}));
+
+ assertExpectException(
+ RuntimeException.class,
+ "activity cannot be null",
+ () -> new ShortcutInfo.Builder(getTestContext(), "id").setActivity(null));
+
+ assertExpectException(
+ RuntimeException.class,
+ "shortLabel cannot be empty",
+ () -> new ShortcutInfo.Builder(getTestContext(), "id").setShortLabel(null));
+
+ assertExpectException(
+ RuntimeException.class,
+ "shortLabel cannot be empty",
+ () -> new ShortcutInfo.Builder(getTestContext(), "id").setShortLabel(""));
+
+ assertExpectException(
+ RuntimeException.class,
+ "longLabel cannot be empty",
+ () -> new ShortcutInfo.Builder(getTestContext(), "id").setLongLabel(null));
+
+ assertExpectException(
+ RuntimeException.class,
+ "longLabel cannot be empty",
+ () -> new ShortcutInfo.Builder(getTestContext(), "id").setLongLabel(""));
+
+ assertExpectException(
+ RuntimeException.class,
+ "disabledMessage cannot be empty",
+ () -> new ShortcutInfo.Builder(getTestContext(), "id").setDisabledMessage(null));
+
+ assertExpectException(
+ RuntimeException.class,
+ "disabledMessage cannot be empty",
+ () -> new ShortcutInfo.Builder(getTestContext(), "id").setDisabledMessage(""));
+
+ assertExpectException(NullPointerException.class, "action must be set",
+ () -> new ShortcutInfo.Builder(getTestContext(), "id").setIntent(new Intent()));
+
+ assertExpectException(
+ IllegalArgumentException.class, "Short label must be provided", () -> {
+ ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
+ .setActivity(new ComponentName(getTestContext().getPackageName(), "s"))
+ .build();
+ assertTrue(getManager().setDynamicShortcuts(list(si)));
+ });
+
+ // same for add.
+ assertExpectException(
+ IllegalArgumentException.class, "Short label must be provided", () -> {
+ ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
+ .setActivity(new ComponentName(getTestContext().getPackageName(), "s"))
+ .build();
+ assertTrue(getManager().addDynamicShortcuts(list(si)));
+ });
+
+ assertExpectException(NullPointerException.class, "Intent must be provided", () -> {
+ ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
+ .setActivity(new ComponentName(getTestContext().getPackageName(), "s"))
+ .setShortLabel("x")
+ .build();
+ assertTrue(getManager().setDynamicShortcuts(list(si)));
+ });
+
+ // same for add.
+ assertExpectException(NullPointerException.class, "Intent must be provided", () -> {
+ ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
+ .setActivity(new ComponentName(getTestContext().getPackageName(), "s"))
+ .setShortLabel("x")
+ .build();
+ assertTrue(getManager().addDynamicShortcuts(list(si)));
+ });
+
+ assertExpectException(
+ IllegalStateException.class, "does not belong to package", () -> {
+ ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
+ .setActivity(new ComponentName("xxx", "s"))
+ .build();
+ assertTrue(getManager().setDynamicShortcuts(list(si)));
+ });
+
+ // same for add.
+ assertExpectException(
+ IllegalStateException.class, "does not belong to package", () -> {
+ ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
+ .setActivity(new ComponentName("xxx", "s"))
+ .build();
+ assertTrue(getManager().addDynamicShortcuts(list(si)));
+ });
+
+ // Now all activities are not main.
+ mMainActivityChecker = (component, userId) -> false;
+
+ assertExpectException(
+ IllegalStateException.class, "is not main", () -> {
+ ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
+ .setActivity(new ComponentName(getTestContext(), "s"))
+ .build();
+ assertTrue(getManager().setDynamicShortcuts(list(si)));
+ });
+ // For add
+ assertExpectException(
+ IllegalStateException.class, "is not main", () -> {
+ ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
+ .setActivity(new ComponentName(getTestContext(), "s"))
+ .build();
+ assertTrue(getManager().addDynamicShortcuts(list(si)));
+ });
+ // For update
+ assertExpectException(
+ IllegalStateException.class, "is not main", () -> {
+ ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
+ .setActivity(new ComponentName(getTestContext(), "s"))
+ .build();
+ assertTrue(getManager().updateShortcuts(list(si)));
+ });
+ }
+
+ public void testShortcutInfoParcel() {
+ setCaller(CALLING_PACKAGE_1, USER_10);
+ ShortcutInfo si = parceled(new ShortcutInfo.Builder(mClientContext)
+ .setId("id")
+ .setTitle("title")
+ .setIntent(makeIntent("action", ShortcutActivity.class))
+ .build());
+ assertEquals(mClientContext.getPackageName(), si.getPackage());
+ assertEquals(USER_10, si.getUserId());
+ assertEquals(HANDLE_USER_10, si.getUserHandle());
+ assertEquals("id", si.getId());
+ assertEquals("title", si.getTitle());
+ assertEquals("action", si.getIntent().getAction());
+
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+
+ si = new ShortcutInfo.Builder(getTestContext())
+ .setId("id")
+ .setActivity(new ComponentName("a", "b"))
+ .setIcon(Icon.createWithResource(mClientContext, 123))
+ .setTitle("title")
+ .setText("text")
+ .setDisabledMessage("dismes")
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
+ .setRank(123)
+ .setExtras(pb)
+ .build();
+ si.addFlags(ShortcutInfo.FLAG_PINNED);
+ si.setBitmapPath("abc");
+ si.setIconResourceId(456);
+
+ si = parceled(si);
+
+ assertEquals(getTestContext().getPackageName(), si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals(new ComponentName("a", "b"), si.getActivity());
+ assertEquals(123, si.getIcon().getResId());
+ assertEquals("title", si.getTitle());
+ assertEquals("text", si.getText());
+ assertEquals("dismes", si.getDisabledMessage());
+ assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("val", si.getIntent().getStringExtra("key"));
+ assertEquals(123, si.getRank());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+ assertEquals("abc", si.getBitmapPath());
+ assertEquals(456, si.getIconResourceId());
+
+ assertEquals(0, si.getTitleResId());
+ assertEquals(null, si.getTitleResName());
+ assertEquals(0, si.getTextResId());
+ assertEquals(null, si.getTextResName());
+ assertEquals(0, si.getDisabledMessageResourceId());
+ assertEquals(null, si.getDisabledMessageResName());
+ }
+
+ public void testShortcutInfoParcel_resId() {
+ setCaller(CALLING_PACKAGE_1, USER_10);
+ ShortcutInfo si;
+
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+
+ si = new ShortcutInfo.Builder(getTestContext())
+ .setId("id")
+ .setActivity(new ComponentName("a", "b"))
+ .setIcon(Icon.createWithResource(mClientContext, 123))
+ .setTitleResId(10)
+ .setTextResId(11)
+ .setDisabledMessageResId(12)
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
+ .setRank(123)
+ .setExtras(pb)
+ .build();
+ si.addFlags(ShortcutInfo.FLAG_PINNED);
+ si.setBitmapPath("abc");
+ si.setIconResourceId(456);
+
+ lookupAndFillInResourceNames(si);
+
+ si = parceled(si);
+
+ assertEquals(getTestContext().getPackageName(), si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals(new ComponentName("a", "b"), si.getActivity());
+ assertEquals(123, si.getIcon().getResId());
+ assertEquals(10, si.getTitleResId());
+ assertEquals("r10", si.getTitleResName());
+ assertEquals(11, si.getTextResId());
+ assertEquals("r11", si.getTextResName());
+ assertEquals(12, si.getDisabledMessageResourceId());
+ assertEquals("r12", si.getDisabledMessageResName());
+ assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("val", si.getIntent().getStringExtra("key"));
+ assertEquals(123, si.getRank());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+ assertEquals("abc", si.getBitmapPath());
+ assertEquals(456, si.getIconResourceId());
+ assertEquals("string/r456", si.getIconResName());
+ }
+
+ public void testShortcutInfoClone() {
+ setCaller(CALLING_PACKAGE_1, USER_11);
+
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+ ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
+ .setId("id")
+ .setActivity(new ComponentName("a", "b"))
+ .setIcon(Icon.createWithResource(mClientContext, 123))
+ .setTitle("title")
+ .setText("text")
+ .setDisabledMessage("dismes")
+ .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setRank(123)
+ .setExtras(pb)
+ .build();
+ sorig.addFlags(ShortcutInfo.FLAG_PINNED);
+ sorig.setBitmapPath("abc");
+ sorig.setIconResourceId(456);
+
+ lookupAndFillInResourceNames(sorig);
+
+ ShortcutInfo si = sorig.clone(/* clone flags*/ 0);
+
+ assertEquals(USER_11, si.getUserId());
+ assertEquals(HANDLE_USER_11, si.getUserHandle());
+ assertEquals(mClientContext.getPackageName(), si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals(new ComponentName("a", "b"), si.getActivity());
+ assertEquals(123, si.getIcon().getResId());
+ assertEquals("title", si.getTitle());
+ assertEquals("text", si.getText());
+ assertEquals("dismes", si.getDisabledMessage());
+ assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("val", si.getIntent().getStringExtra("key"));
+ assertEquals(123, si.getRank());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+ assertEquals("abc", si.getBitmapPath());
+ assertEquals(456, si.getIconResourceId());
+ assertEquals("string/r456", si.getIconResName());
+
+ si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
+
+ assertEquals(mClientContext.getPackageName(), si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals(new ComponentName("a", "b"), si.getActivity());
+ assertEquals(null, si.getIcon());
+ assertEquals("title", si.getTitle());
+ assertEquals("text", si.getText());
+ assertEquals("dismes", si.getDisabledMessage());
+ assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("val", si.getIntent().getStringExtra("key"));
+ assertEquals(123, si.getRank());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+ assertEquals(null, si.getBitmapPath());
+
+ assertEquals(456, si.getIconResourceId());
+ assertEquals(null, si.getIconResName());
+
+ si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+
+ assertEquals(mClientContext.getPackageName(), si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals(new ComponentName("a", "b"), si.getActivity());
+ assertEquals(null, si.getIcon());
+ assertEquals("title", si.getTitle());
+ assertEquals("text", si.getText());
+ assertEquals("dismes", si.getDisabledMessage());
+ assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
+ assertEquals(null, si.getIntent());
+ assertEquals(123, si.getRank());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+ assertEquals(null, si.getBitmapPath());
+
+ assertEquals(456, si.getIconResourceId());
+ assertEquals(null, si.getIconResName());
+
+ si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
+
+ assertEquals(mClientContext.getPackageName(), si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals(new ComponentName("a", "b"), si.getActivity());
+ assertEquals(null, si.getIcon());
+ assertEquals(null, si.getTitle());
+ assertEquals(null, si.getText());
+ assertEquals(null, si.getDisabledMessage());
+ assertEquals(null, si.getCategories());
+ assertEquals(null, si.getIntent());
+ assertEquals(0, si.getRank());
+ assertEquals(null, si.getExtras());
+
+ assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY, si.getFlags());
+ assertEquals(null, si.getBitmapPath());
+
+ assertEquals(456, si.getIconResourceId());
+ assertEquals(null, si.getIconResName());
+ }
+
+ public void testShortcutInfoClone_resId() {
+ setCaller(CALLING_PACKAGE_1, USER_11);
+
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+ ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
+ .setId("id")
+ .setActivity(new ComponentName("a", "b"))
+ .setIcon(Icon.createWithResource(mClientContext, 123))
+ .setTitleResId(10)
+ .setTextResId(11)
+ .setDisabledMessageResId(12)
+ .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setRank(123)
+ .setExtras(pb)
+ .build();
+ sorig.addFlags(ShortcutInfo.FLAG_PINNED);
+ sorig.setBitmapPath("abc");
+ sorig.setIconResourceId(456);
+
+ lookupAndFillInResourceNames(sorig);
+
+ ShortcutInfo si = sorig.clone(/* clone flags*/ 0);
+
+ assertEquals(USER_11, si.getUserId());
+ assertEquals(HANDLE_USER_11, si.getUserHandle());
+ assertEquals(mClientContext.getPackageName(), si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals(new ComponentName("a", "b"), si.getActivity());
+ assertEquals(123, si.getIcon().getResId());
+ assertEquals(10, si.getTitleResId());
+ assertEquals("r10", si.getTitleResName());
+ assertEquals(11, si.getTextResId());
+ assertEquals("r11", si.getTextResName());
+ assertEquals(12, si.getDisabledMessageResourceId());
+ assertEquals("r12", si.getDisabledMessageResName());
+ assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("val", si.getIntent().getStringExtra("key"));
+ assertEquals(123, si.getRank());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+ assertEquals("abc", si.getBitmapPath());
+ assertEquals(456, si.getIconResourceId());
+ assertEquals("string/r456", si.getIconResName());
+
+ si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
+
+ assertEquals(mClientContext.getPackageName(), si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals(new ComponentName("a", "b"), si.getActivity());
+ assertEquals(null, si.getIcon());
+ assertEquals(10, si.getTitleResId());
+ assertEquals(null, si.getTitleResName());
+ assertEquals(11, si.getTextResId());
+ assertEquals(null, si.getTextResName());
+ assertEquals(12, si.getDisabledMessageResourceId());
+ assertEquals(null, si.getDisabledMessageResName());
+ assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("val", si.getIntent().getStringExtra("key"));
+ assertEquals(123, si.getRank());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+ assertEquals(null, si.getBitmapPath());
+
+ assertEquals(456, si.getIconResourceId());
+ assertEquals(null, si.getIconResName());
+
+ si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+
+ assertEquals(mClientContext.getPackageName(), si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals(new ComponentName("a", "b"), si.getActivity());
+ assertEquals(null, si.getIcon());
+ assertEquals(10, si.getTitleResId());
+ assertEquals(null, si.getTitleResName());
+ assertEquals(11, si.getTextResId());
+ assertEquals(null, si.getTextResName());
+ assertEquals(12, si.getDisabledMessageResourceId());
+ assertEquals(null, si.getDisabledMessageResName());
+ assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
+ assertEquals(null, si.getIntent());
+ assertEquals(123, si.getRank());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+ assertEquals(null, si.getBitmapPath());
+
+ assertEquals(456, si.getIconResourceId());
+ assertEquals(null, si.getIconResName());
+
+ si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
+
+ assertEquals(mClientContext.getPackageName(), si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals(new ComponentName("a", "b"), si.getActivity());
+ assertEquals(null, si.getIcon());
+ assertEquals(0, si.getTitleResId());
+ assertEquals(null, si.getTitleResName());
+ assertEquals(0, si.getTextResId());
+ assertEquals(null, si.getTextResName());
+ assertEquals(0, si.getDisabledMessageResourceId());
+ assertEquals(null, si.getDisabledMessageResName());
+ assertEquals(null, si.getCategories());
+ assertEquals(null, si.getIntent());
+ assertEquals(0, si.getRank());
+ assertEquals(null, si.getExtras());
+
+ assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY, si.getFlags());
+ assertEquals(null, si.getBitmapPath());
+
+ assertEquals(456, si.getIconResourceId());
+ assertEquals(null, si.getIconResName());
+ }
+
+ public void testShortcutInfoClone_minimum() {
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+ ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext())
+ .setId("id")
+ .setTitle("title")
+ .setIntent(makeIntent("action", ShortcutActivity.class))
+ .build();
+ ShortcutInfo si = sorig.clone(/* clone flags*/ 0);
+
+ assertEquals(getTestContext().getPackageName(), si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals("title", si.getTitle());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals(null, si.getCategories());
+
+ si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
+
+ assertEquals(getTestContext().getPackageName(), si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals("title", si.getTitle());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals(null, si.getCategories());
+
+ si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+
+ assertEquals(getTestContext().getPackageName(), si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals("title", si.getTitle());
+ assertEquals(null, si.getIntent());
+ assertEquals(null, si.getCategories());
+
+ si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
+
+ assertEquals(getTestContext().getPackageName(), si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals(null, si.getTitle());
+ assertEquals(null, si.getIntent());
+ assertEquals(null, si.getCategories());
+ }
+
+ public void testShortcutInfoCopyNonNullFieldsFrom() throws InterruptedException {
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+ ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext())
+ .setId("id")
+ .setActivity(new ComponentName("a", "b"))
+ .setIcon(Icon.createWithResource(mClientContext, 123))
+ .setTitle("title")
+ .setText("text")
+ .setDisabledMessage("dismes")
+ .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setRank(123)
+ .setExtras(pb)
+ .build();
+ sorig.addFlags(ShortcutInfo.FLAG_PINNED);
+ sorig.setBitmapPath("abc");
+ sorig.setIconResourceId(456);
+
+ lookupAndFillInResourceNames(sorig);
+
+ ShortcutInfo si;
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setActivity(new ComponentName("x", "y")).build());
+ assertEquals("text", si.getText());
+ assertEquals(123, si.getRank());
+ assertEquals(new ComponentName("x", "y"), si.getActivity());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setIcon(Icon.createWithResource(mClientContext, 456)).build());
+ assertEquals("text", si.getText());
+ assertEquals(456, si.getIcon().getResId());
+ assertEquals(0, si.getIconResourceId());
+ assertEquals(null, si.getIconResName());
+ assertEquals(null, si.getBitmapPath());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setTitle("xyz").build());
+ assertEquals("text", si.getText());
+ assertEquals("xyz", si.getTitle());
+ assertEquals(0, si.getTitleResId());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setTitleResId(123).build());
+ assertEquals("text", si.getText());
+ assertEquals(null, si.getTitle());
+ assertEquals(123, si.getTitleResId());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setText("xxx").build());
+ assertEquals(123, si.getRank());
+ assertEquals("xxx", si.getText());
+ assertEquals(0, si.getTextResId());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setTextResId(1111).build());
+ assertEquals(123, si.getRank());
+ assertEquals(null, si.getText());
+ assertEquals(1111, si.getTextResId());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setDisabledMessage("xxx").build());
+ assertEquals(123, si.getRank());
+ assertEquals("xxx", si.getDisabledMessage());
+ assertEquals(0, si.getDisabledMessageResourceId());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setDisabledMessageResId(11111).build());
+ assertEquals(123, si.getRank());
+ assertEquals(null, si.getDisabledMessage());
+ assertEquals(11111, si.getDisabledMessageResourceId());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setCategories(set()).build());
+ assertEquals("text", si.getText());
+ assertEquals(set(), si.getCategories());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setCategories(set("x")).build());
+ assertEquals("text", si.getText());
+ assertEquals(set("x"), si.getCategories());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setIntent(makeIntent("action2", ShortcutActivity.class)).build());
+ assertEquals("text", si.getText());
+ assertEquals("action2", si.getIntent().getAction());
+ assertEquals(null, si.getIntent().getStringExtra("key"));
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setIntent(makeIntent("action3", ShortcutActivity.class, "key", "x")).build());
+ assertEquals("text", si.getText());
+ assertEquals("action3", si.getIntent().getAction());
+ assertEquals("x", si.getIntent().getStringExtra("key"));
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setRank(999).build());
+ assertEquals("text", si.getText());
+ assertEquals(999, si.getRank());
+
+
+ PersistableBundle pb2 = new PersistableBundle();
+ pb2.putInt("x", 99);
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setExtras(pb2).build());
+ assertEquals("text", si.getText());
+ assertEquals(99, si.getExtras().getInt("x"));
+ }
+
+ public void testShortcutInfoCopyNonNullFieldsFrom_resId() throws InterruptedException {
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+ ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext())
+ .setId("id")
+ .setActivity(new ComponentName("a", "b"))
+ .setIcon(Icon.createWithResource(mClientContext, 123))
+ .setTitleResId(10)
+ .setTextResId(11)
+ .setDisabledMessageResId(12)
+ .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setRank(123)
+ .setExtras(pb)
+ .build();
+ sorig.addFlags(ShortcutInfo.FLAG_PINNED);
+ sorig.setBitmapPath("abc");
+ sorig.setIconResourceId(456);
+
+ ShortcutInfo si;
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setActivity(new ComponentName("x", "y")).build());
+ assertEquals(11, si.getTextResId());
+ assertEquals(new ComponentName("x", "y"), si.getActivity());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setIcon(Icon.createWithResource(mClientContext, 456)).build());
+ assertEquals(11, si.getTextResId());
+ assertEquals(456, si.getIcon().getResId());
+ assertEquals(0, si.getIconResourceId());
+ assertEquals(null, si.getIconResName());
+ assertEquals(null, si.getBitmapPath());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setTitle("xyz").build());
+ assertEquals(11, si.getTextResId());
+ assertEquals("xyz", si.getTitle());
+ assertEquals(0, si.getTitleResId());
+ assertEquals(null, si.getTitleResName());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setTitleResId(123).build());
+ assertEquals(11, si.getTextResId());
+ assertEquals(null, si.getTitle());
+ assertEquals(123, si.getTitleResId());
+ assertEquals(null, si.getTitleResName());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setText("xxx").build());
+ assertEquals(123, si.getRank());
+ assertEquals("xxx", si.getText());
+ assertEquals(0, si.getTextResId());
+ assertEquals(null, si.getTextResName());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setTextResId(1111).build());
+ assertEquals(123, si.getRank());
+ assertEquals(null, si.getText());
+ assertEquals(1111, si.getTextResId());
+ assertEquals(null, si.getTextResName());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setDisabledMessage("xxx").build());
+ assertEquals(123, si.getRank());
+ assertEquals("xxx", si.getDisabledMessage());
+ assertEquals(0, si.getDisabledMessageResourceId());
+ assertEquals(null, si.getDisabledMessageResName());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setDisabledMessageResId(11111).build());
+ assertEquals(123, si.getRank());
+ assertEquals(null, si.getDisabledMessage());
+ assertEquals(11111, si.getDisabledMessageResourceId());
+ assertEquals(null, si.getDisabledMessageResName());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setCategories(set()).build());
+ assertEquals(11, si.getTextResId());
+ assertEquals(set(), si.getCategories());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setCategories(set("x")).build());
+ assertEquals(11, si.getTextResId());
+ assertEquals(set("x"), si.getCategories());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setIntent(makeIntent("action2", ShortcutActivity.class)).build());
+ assertEquals(11, si.getTextResId());
+ assertEquals("action2", si.getIntent().getAction());
+ assertEquals(null, si.getIntent().getStringExtra("key"));
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setIntent(makeIntent("action3", ShortcutActivity.class, "key", "x")).build());
+ assertEquals(11, si.getTextResId());
+ assertEquals("action3", si.getIntent().getAction());
+ assertEquals("x", si.getIntent().getStringExtra("key"));
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setRank(999).build());
+ assertEquals(11, si.getTextResId());
+ assertEquals(999, si.getRank());
+
+
+ PersistableBundle pb2 = new PersistableBundle();
+ pb2.putInt("x", 99);
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setExtras(pb2).build());
+ assertEquals(11, si.getTextResId());
+ assertEquals(99, si.getExtras().getInt("x"));
+ }
+
+ public void testShortcutInfoSaveAndLoad() throws InterruptedException {
+ mRunningUsers.put(USER_10, true);
+
+ setCaller(CALLING_PACKAGE_1, USER_10);
+
+ final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.black_32x32));
+
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+ ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
+ .setId("id")
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setIcon(bmp32x32)
+ .setTitle("title")
+ .setText("text")
+ .setDisabledMessage("dismes")
+ .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setRank(123)
+ .setExtras(pb)
+ .build();
+ sorig.setTimestamp(mInjectedCurrentTimeMillis);
+
+ ShortcutInfo sorig2 = new ShortcutInfo.Builder(mClientContext)
+ .setId("id2")
+ .setTitle("x")
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setRank(456)
+ .build();
+ sorig2.setTimestamp(mInjectedCurrentTimeMillis);
+
+ mManager.addDynamicShortcuts(list(sorig, sorig2));
+
+ mInjectedCurrentTimeMillis += 1;
+ final long now = mInjectedCurrentTimeMillis;
+ mInjectedCurrentTimeMillis += 1;
+
+ dumpsysOnLogcat("before save");
+
+ // Save and load.
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_10);
+
+ dumpUserFile(USER_10);
+ dumpsysOnLogcat("after load");
+
+ ShortcutInfo si;
+ si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_10);
+
+ assertEquals(USER_10, si.getUserId());
+ assertEquals(HANDLE_USER_10, si.getUserHandle());
+ assertEquals(CALLING_PACKAGE_1, si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName());
+ assertEquals(null, si.getIcon());
+ assertEquals("title", si.getTitle());
+ assertEquals("text", si.getText());
+ assertEquals("dismes", si.getDisabledMessage());
+ assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("val", si.getIntent().getStringExtra("key"));
+ assertEquals(0, si.getRank());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_FILE
+ | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
+ assertNotNull(si.getBitmapPath()); // Something should be set.
+ assertEquals(0, si.getIconResourceId());
+ assertTrue(si.getLastChangedTimestamp() < now);
+
+ // Make sure ranks are saved too. Because of the auto-adjusting, we need two shortcuts
+ // to test it.
+ si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_10);
+ assertEquals(1, si.getRank());
+
+ dumpUserFile(USER_10);
+ }
+
+ public void testShortcutInfoSaveAndLoad_resId() throws InterruptedException {
+ mRunningUsers.put(USER_10, true);
+
+ setCaller(CALLING_PACKAGE_1, USER_10);
+
+ final Icon res32x32 = Icon.createWithResource(mClientContext, R.drawable.black_32x32);
+
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+ ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
+ .setId("id")
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setIcon(res32x32)
+ .setTitleResId(10)
+ .setTextResId(11)
+ .setDisabledMessageResId(12)
+ .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setRank(123)
+ .setExtras(pb)
+ .build();
+ sorig.setTimestamp(mInjectedCurrentTimeMillis);
+
+ ShortcutInfo sorig2 = new ShortcutInfo.Builder(mClientContext)
+ .setId("id2")
+ .setTitle("x")
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setRank(456)
+ .build();
+ sorig2.setTimestamp(mInjectedCurrentTimeMillis);
+
+ mManager.addDynamicShortcuts(list(sorig, sorig2));
+
+ mInjectedCurrentTimeMillis += 1;
+ final long now = mInjectedCurrentTimeMillis;
+ mInjectedCurrentTimeMillis += 1;
+
+ // Save and load.
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_10);
+
+ ShortcutInfo si;
+ si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_10);
+
+ assertEquals(USER_10, si.getUserId());
+ assertEquals(HANDLE_USER_10, si.getUserHandle());
+ assertEquals(CALLING_PACKAGE_1, si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName());
+ assertEquals(null, si.getIcon());
+ assertEquals(10, si.getTitleResId());
+ assertEquals("r10", si.getTitleResName());
+ assertEquals(11, si.getTextResId());
+ assertEquals("r11", si.getTextResName());
+ assertEquals(12, si.getDisabledMessageResourceId());
+ assertEquals("r12", si.getDisabledMessageResName());
+ assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("val", si.getIntent().getStringExtra("key"));
+ assertEquals(0, si.getRank());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_RES
+ | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
+ assertNull(si.getBitmapPath());
+ assertEquals(R.drawable.black_32x32, si.getIconResourceId());
+ assertTrue(si.getLastChangedTimestamp() < now);
+
+ // Make sure ranks are saved too. Because of the auto-adjusting, we need two shortcuts
+ // to test it.
+ si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_10);
+ assertEquals(1, si.getRank());
+ }
+
+ public void testShortcutInfoSaveAndLoad_forBackup() {
+ setCaller(CALLING_PACKAGE_1, USER_0);
+
+ final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.black_32x32));
+
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+ ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
+ .setId("id")
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setIcon(bmp32x32)
+ .setTitle("title")
+ .setText("text")
+ .setDisabledMessage("dismes")
+ .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setRank(123)
+ .setExtras(pb)
+ .build();
+
+ ShortcutInfo sorig2 = new ShortcutInfo.Builder(mClientContext)
+ .setId("id2")
+ .setTitle("x")
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setRank(456)
+ .build();
+
+ mManager.addDynamicShortcuts(list(sorig, sorig2));
+
+ // Dynamic shortcuts won't be backed up, so we need to pin it.
+ setCaller(LAUNCHER_1, USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("id", "id2"), HANDLE_USER_0);
+
+ // Do backup & restore.
+ backupAndRestore();
+
+ mService.handleUnlockUser(USER_0); // Load user-0.
+
+ ShortcutInfo si;
+ si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0);
+
+ assertEquals(CALLING_PACKAGE_1, si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName());
+ assertEquals(null, si.getIcon());
+ assertEquals("title", si.getTitle());
+ assertEquals("text", si.getText());
+ assertEquals("dismes", si.getDisabledMessage());
+ assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("val", si.getIntent().getStringExtra("key"));
+ assertEquals(0, si.getRank());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
+ assertNull(si.getBitmapPath()); // No icon.
+ assertEquals(0, si.getIconResourceId());
+
+ // Note when restored from backup, it's no longer dynamic, so shouldn't have a rank.
+ si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_0);
+ assertEquals(0, si.getRank());
+ }
+
+ public void testShortcutInfoSaveAndLoad_forBackup_resId() {
+ setCaller(CALLING_PACKAGE_1, USER_0);
+
+ final Icon res32x32 = Icon.createWithResource(mClientContext, R.drawable.black_32x32);
+
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+ ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
+ .setId("id")
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setIcon(res32x32)
+ .setTitleResId(10)
+ .setTextResId(11)
+ .setDisabledMessageResId(12)
+ .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setRank(123)
+ .setExtras(pb)
+ .build();
+
+ ShortcutInfo sorig2 = new ShortcutInfo.Builder(mClientContext)
+ .setId("id2")
+ .setTitle("x")
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setRank(456)
+ .build();
+
+ mManager.addDynamicShortcuts(list(sorig, sorig2));
+
+ // Dynamic shortcuts won't be backed up, so we need to pin it.
+ setCaller(LAUNCHER_1, USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("id", "id2"), HANDLE_USER_0);
+
+ // Do backup & restore.
+ backupAndRestore();
+
+ mService.handleUnlockUser(USER_0); // Load user-0.
+
+ ShortcutInfo si;
+ si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0);
+
+ assertEquals(CALLING_PACKAGE_1, si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName());
+ assertEquals(null, si.getIcon());
+ assertEquals(10, si.getTitleResId());
+ assertEquals("r10", si.getTitleResName());
+ assertEquals(11, si.getTextResId());
+ assertEquals("r11", si.getTextResName());
+ assertEquals(12, si.getDisabledMessageResourceId());
+ assertEquals("r12", si.getDisabledMessageResName());
+ assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("val", si.getIntent().getStringExtra("key"));
+ assertEquals(0, si.getRank());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
+ assertNull(si.getBitmapPath()); // No icon.
+ assertEquals(0, si.getIconResourceId());
+ assertEquals(null, si.getIconResName());
+
+ // Note when restored from backup, it's no longer dynamic, so shouldn't have a rank.
+ si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_0);
+ assertEquals(0, si.getRank());
+ }
+
+ private void checkShortcutInfoSaveAndLoad_intents(Intent intent) {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcutWithIntent("s1", intent))));
+ initService();
+ mService.handleUnlockUser(USER_0);
+
+ assertWith(getCallerShortcuts())
+ .haveIds("s1")
+ .forShortcutWithId("s1", si -> {
+ assertEquals(intent.getAction(), si.getIntent().getAction());
+ assertEquals(intent.getData(), si.getIntent().getData());
+ assertEquals(intent.getComponent(), si.getIntent().getComponent());
+ assertBundlesEqual(intent.getExtras(), si.getIntent().getExtras());
+ });
+ }
+
+ private void checkShortcutInfoSaveAndLoad_intents(Intent... intents) {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcutWithIntents("s1", intents))));
+ initService();
+ mService.handleUnlockUser(USER_0);
+
+ assertWith(getCallerShortcuts())
+ .haveIds("s1")
+ .forShortcutWithId("s1", si -> {
+
+ final Intent[] actual = si.getIntents();
+ assertEquals(intents.length, actual.length);
+
+ for (int i = 0; i < intents.length; i++) {
+ assertEquals(intents[i].getAction(), actual[i].getAction());
+ assertEquals(intents[i].getData(), actual[i].getData());
+ assertEquals(intents[i].getComponent(), actual[i].getComponent());
+ assertEquals(intents[i].getFlags(), actual[i].getFlags());
+ assertBundlesEqual(intents[i].getExtras(), actual[i].getExtras());
+ }
+ });
+ }
+
+ public void testShortcutInfoSaveAndLoad_intents() {
+ checkShortcutInfoSaveAndLoad_intents(new Intent(Intent.ACTION_VIEW));
+
+ mInjectedCurrentTimeMillis += INTERVAL; // reset throttling.
+
+ checkShortcutInfoSaveAndLoad_intents(new Intent(Intent.ACTION_MAIN));
+
+ mInjectedCurrentTimeMillis += INTERVAL; // reset throttling.
+
+ checkShortcutInfoSaveAndLoad_intents(new Intent(Intent.ACTION_VIEW,
+ Uri.parse("http://www.example.com/")));
+
+ mInjectedCurrentTimeMillis += INTERVAL; // reset throttling.
+
+ checkShortcutInfoSaveAndLoad_intents(new Intent(Intent.ACTION_MAIN,
+ Uri.parse("http://www.example.com/")));
+
+ mInjectedCurrentTimeMillis += INTERVAL; // reset throttling.
+
+ checkShortcutInfoSaveAndLoad_intents(new Intent(Intent.ACTION_VIEW)
+ .setComponent(new ComponentName("a", "b")));
+
+ mInjectedCurrentTimeMillis += INTERVAL; // reset throttling.
+
+ checkShortcutInfoSaveAndLoad_intents(new Intent(Intent.ACTION_MAIN)
+ .setComponent(new ComponentName("a", "b")));
+
+ mInjectedCurrentTimeMillis += INTERVAL; // reset throttling.
+
+ checkShortcutInfoSaveAndLoad_intents(new Intent(Intent.ACTION_VIEW)
+ .putExtras(makeBundle("a", "b")));
+
+ mInjectedCurrentTimeMillis += INTERVAL; // reset throttling.
+
+
+ checkShortcutInfoSaveAndLoad_intents(new Intent(Intent.ACTION_MAIN)
+ .putExtras(makeBundle("a", "b")));
+
+ mInjectedCurrentTimeMillis += INTERVAL; // reset throttling.
+
+ // Multi-intents
+ checkShortcutInfoSaveAndLoad_intents(
+ new Intent(Intent.ACTION_MAIN).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK),
+ new Intent(Intent.ACTION_VIEW).setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
+ );
+
+ checkShortcutInfoSaveAndLoad_intents(
+ new Intent(Intent.ACTION_MAIN).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ .setComponent(new ComponentName("a", "b")),
+ new Intent(Intent.ACTION_VIEW)
+ .setComponent(new ComponentName("a", "b"))
+ );
+
+ checkShortcutInfoSaveAndLoad_intents(
+ new Intent(Intent.ACTION_MAIN)
+ .setComponent(new ComponentName("a", "b")),
+ new Intent(Intent.ACTION_VIEW).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ .setComponent(new ComponentName("a", "b")),
+ new Intent("xyz").setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FILL_IN_COMPONENT)
+ .setComponent(new ComponentName("a", "b")).putExtras(
+ makeBundle("xx", "yy"))
+ );
+ }
+
+ public void testThrottling() {
+ final ShortcutInfo si1 = makeShortcut("shortcut1");
+
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertEquals(2, mManager.getRemainingCallCount());
+ assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+
+ mInjectedCurrentTimeMillis++;
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertEquals(1, mManager.getRemainingCallCount());
+ assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+
+ mInjectedCurrentTimeMillis++;
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertEquals(0, mManager.getRemainingCallCount());
+ assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+
+ // Reached the max
+
+ mInjectedCurrentTimeMillis++;
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
+ assertEquals(0, mManager.getRemainingCallCount());
+ assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+
+ // Still throttled
+ mInjectedCurrentTimeMillis = START_TIME + INTERVAL - 1;
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
+ assertEquals(0, mManager.getRemainingCallCount());
+ assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+
+ // Now it should work.
+ mInjectedCurrentTimeMillis++;
+ assertTrue(mManager.setDynamicShortcuts(list(si1))); // fail
+ assertEquals(2, mManager.getRemainingCallCount());
+ assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
+
+ mInjectedCurrentTimeMillis++;
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertEquals(1, mManager.getRemainingCallCount());
+ assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
+
+ mInjectedCurrentTimeMillis++;
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertEquals(0, mManager.getRemainingCallCount());
+ assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
+
+ mInjectedCurrentTimeMillis++;
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
+ assertEquals(0, mManager.getRemainingCallCount());
+ assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
+
+ // 4 hours later...
+ mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL;
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertEquals(2, mManager.getRemainingCallCount());
+ assertEquals(START_TIME + INTERVAL * 5, mManager.getRateLimitResetTime());
+
+ mInjectedCurrentTimeMillis++;
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertEquals(1, mManager.getRemainingCallCount());
+ assertEquals(START_TIME + INTERVAL * 5, mManager.getRateLimitResetTime());
+
+ // Make sure getRemainingCallCount() itself gets reset without calling setDynamicShortcuts().
+ mInjectedCurrentTimeMillis = START_TIME + 8 * INTERVAL;
+ assertEquals(3, mManager.getRemainingCallCount());
+ assertEquals(START_TIME + INTERVAL * 9, mManager.getRateLimitResetTime());
+
+ mInjectedCurrentTimeMillis++;
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertEquals(2, mManager.getRemainingCallCount());
+ assertEquals(START_TIME + INTERVAL * 9, mManager.getRateLimitResetTime());
+ }
+
+ public void testThrottling_rewind() {
+ final ShortcutInfo si1 = makeShortcut("shortcut1");
+
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertEquals(2, mManager.getRemainingCallCount());
+ assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+
+ mInjectedCurrentTimeMillis = 12345; // Clock reset!
+
+ // Since the clock looks invalid, the counter shouldn't have reset.
+ assertEquals(2, mManager.getRemainingCallCount());
+ assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+
+ // Forward again. Still haven't reset yet.
+ mInjectedCurrentTimeMillis = START_TIME + INTERVAL - 1;
+ assertEquals(2, mManager.getRemainingCallCount());
+ assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+
+ // Now rewind -- this will reset the counters.
+ mInjectedCurrentTimeMillis = START_TIME - 100000;
+ assertEquals(3, mManager.getRemainingCallCount());
+
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ // Forward again, should be reset now.
+ mInjectedCurrentTimeMillis += INTERVAL;
+ assertEquals(3, mManager.getRemainingCallCount());
+ }
+
+ public void testThrottling_perPackage() {
+ final ShortcutInfo si1 = makeShortcut("shortcut1");
+
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ mInjectedCurrentTimeMillis++;
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertEquals(1, mManager.getRemainingCallCount());
+
+ mInjectedCurrentTimeMillis++;
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertEquals(0, mManager.getRemainingCallCount());
+
+ // Reached the max
+
+ mInjectedCurrentTimeMillis++;
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
+
+ // Try from a different caller.
+ mInjectedClientPackage = CALLING_PACKAGE_2;
+ mInjectedCallingUid = CALLING_UID_2;
+
+ // Need to create a new one wit the updated package name.
+ final ShortcutInfo si2 = makeShortcut("shortcut1");
+
+ assertEquals(3, mManager.getRemainingCallCount());
+
+ assertTrue(mManager.setDynamicShortcuts(list(si2)));
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ mInjectedCurrentTimeMillis++;
+ assertTrue(mManager.setDynamicShortcuts(list(si2)));
+ assertEquals(1, mManager.getRemainingCallCount());
+
+ // Back to the original caller, still throttled.
+ mInjectedClientPackage = CALLING_PACKAGE_1;
+ mInjectedCallingUid = CALLING_UID_1;
+
+ mInjectedCurrentTimeMillis = START_TIME + INTERVAL - 1;
+ assertEquals(0, mManager.getRemainingCallCount());
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
+ assertEquals(0, mManager.getRemainingCallCount());
+
+ // Now it should work.
+ mInjectedCurrentTimeMillis++;
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+
+ mInjectedCurrentTimeMillis++;
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+
+ mInjectedCurrentTimeMillis++;
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+
+ mInjectedCurrentTimeMillis++;
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
+
+ mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL;
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
+
+ mInjectedClientPackage = CALLING_PACKAGE_2;
+ mInjectedCallingUid = CALLING_UID_2;
+
+ assertEquals(3, mManager.getRemainingCallCount());
+
+ assertTrue(mManager.setDynamicShortcuts(list(si2)));
+ assertTrue(mManager.setDynamicShortcuts(list(si2)));
+ assertTrue(mManager.setDynamicShortcuts(list(si2)));
+ assertFalse(mManager.setDynamicShortcuts(list(si2)));
+ }
+
+ public void testThrottling_localeChanges() {
+ prepareCrossProfileDataSet();
+
+ dumpsysOnLogcat("Before save & load");
+
+ mService.saveDirtyInfo();
+ initService();
+
+ mInjectedLocale = Locale.CHINA;
+ mService.mReceiver.onReceive(mServiceContext, new Intent(Intent.ACTION_LOCALE_CHANGED));
+
+ // Note at this point only user-0 is loaded, and the counters are reset for this user,
+ // but it will work for other users too because we check the locale change at any
+ // API entry point.
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+
+ // Make sure even if we receive ACTION_LOCALE_CHANGED, if the locale hasn't actually
+ // changed, we don't reset throttling.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.updateShortcuts(list());
+ assertEquals(2, mManager.getRemainingCallCount());
+ });
+
+ mService.mReceiver.onReceive(mServiceContext, new Intent(Intent.ACTION_LOCALE_CHANGED));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(2, mManager.getRemainingCallCount()); // Still 2.
+ });
+
+ mService.saveDirtyInfo();
+ initService();
+
+ // The locale should be persisted, so it still shouldn't reset throttling.
+ mService.mReceiver.onReceive(mServiceContext, new Intent(Intent.ACTION_LOCALE_CHANGED));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(2, mManager.getRemainingCallCount()); // Still 2.
+ });
+ }
+
+ public void testThrottling_foreground() throws Exception {
+ prepareCrossProfileDataSet();
+
+ dumpsysOnLogcat("Before save & load");
+
+ mService.saveDirtyInfo();
+ initService();
+
+ // We need to update the current time from time to time, since some of the internal checks
+ // rely on the time being correctly incremented.
+ mInjectedCurrentTimeMillis++;
+
+ // First, all packages have less than 3 (== initial value) remaining calls.
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+
+ mInjectedCurrentTimeMillis++;
+
+ // State changed, but not foreground, so no resetting.
+ mService.mUidObserver.onUidStateChanged(
+ CALLING_UID_1, ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+
+ mInjectedCurrentTimeMillis++;
+
+ // State changed, package1 foreground, reset.
+ mService.mUidObserver.onUidStateChanged(
+ CALLING_UID_1, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ mService.mUidObserver.onUidStateChanged(
+ CALLING_UID_1, ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+
+ mInjectedCurrentTimeMillis++;
+
+ // Different app comes to foreground briefly, and goes back to background.
+ // Now, make sure package 2's counter is reset, even in this case.
+ mService.mUidObserver.onUidStateChanged(
+ CALLING_UID_2, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ mService.mUidObserver.onUidStateChanged(
+ CALLING_UID_2, ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+
+ mInjectedCurrentTimeMillis++;
+
+ // Do the same thing one more time. This would catch the bug with mixuing up
+ // the current time and the elapsed time.
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ mManager.updateShortcuts(list(makeShortcut("s")));
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+
+ mService.mUidObserver.onUidStateChanged(
+ CALLING_UID_2, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ mService.mUidObserver.onUidStateChanged(
+ CALLING_UID_2, ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+
+ mInjectedCurrentTimeMillis++;
+
+ // Package 1 on user-10 comes to foreground.
+ // Now, also try calling some APIs and make sure foreground apps don't get throttled.
+ mService.mUidObserver.onUidStateChanged(
+ UserHandle.getUid(USER_10, CALLING_UID_1),
+ ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ assertFalse(mManager.isRateLimitingActive());
+
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+
+ assertEquals(2, mManager.getRemainingCallCount());
+ assertFalse(mManager.isRateLimitingActive());
+
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+
+ assertEquals(1, mManager.getRemainingCallCount());
+ assertFalse(mManager.isRateLimitingActive());
+
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+
+ assertEquals(0, mManager.getRemainingCallCount());
+ assertTrue(mManager.isRateLimitingActive());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+
+ assertEquals(0, mManager.getRemainingCallCount());
+ assertTrue(mManager.isRateLimitingActive());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+
+ assertEquals(0, mManager.getRemainingCallCount());
+ assertTrue(mManager.isRateLimitingActive());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+
+ assertEquals(0, mManager.getRemainingCallCount());
+ assertTrue(mManager.isRateLimitingActive());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+
+ assertEquals(0, mManager.getRemainingCallCount());
+ assertTrue(mManager.isRateLimitingActive());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+
+ assertEquals(3, mManager.getRemainingCallCount()); // Still 3!
+ assertFalse(mManager.isRateLimitingActive());
+ });
+ }
+
+
+ public void testThrottling_resetByInternalCall() throws Exception {
+ prepareCrossProfileDataSet();
+
+ dumpsysOnLogcat("Before save & load");
+
+ mService.saveDirtyInfo();
+ initService();
+
+ // First, all packages have less than 3 (== initial value) remaining calls.
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+
+ // Simulate a call from sys UI.
+ mCallerPermissions.add(permission.RESET_SHORTCUT_MANAGER_THROTTLING);
+ mManager.onApplicationActive(CALLING_PACKAGE_1, USER_0);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+
+ mManager.onApplicationActive(CALLING_PACKAGE_3, USER_0);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+
+ mManager.onApplicationActive(CALLING_PACKAGE_1, USER_10);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ }
+
+ public void testReportShortcutUsed() {
+ mRunningUsers.put(USER_10, true);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ reset(mMockUsageStatsManagerInternal);
+
+ // Report with an nonexistent shortcut.
+ mManager.reportShortcutUsed("s1");
+ verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage(
+ anyString(), anyString(), anyInt());
+
+ // Publish s2, but s1 still doesn't exist.
+ mManager.setDynamicShortcuts(list(makeShortcut("s2")));
+ mManager.reportShortcutUsed("s1");
+ verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage(
+ anyString(), anyString(), anyInt());
+
+ mManager.reportShortcutUsed("s2");
+ verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+ eq(CALLING_PACKAGE_1), eq("s2"), eq(USER_10));
+
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ // Try with a different package.
+ reset(mMockUsageStatsManagerInternal);
+
+ // Report with an nonexistent shortcut.
+ mManager.reportShortcutUsed("s2");
+ verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage(
+ anyString(), anyString(), anyInt());
+
+ // Publish s2, but s1 still doesn't exist.
+ mManager.setDynamicShortcuts(list(makeShortcut("s3")));
+ mManager.reportShortcutUsed("s2");
+ verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage(
+ anyString(), anyString(), anyInt());
+
+ mManager.reportShortcutUsed("s3");
+ verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+ eq(CALLING_PACKAGE_2), eq("s3"), eq(USER_10));
+
+ });
+ }
+
+ // Test for a ShortcutInfo method.
+ public void testGetResourcePackageName() {
+ assertEquals(null, ShortcutInfo.getResourcePackageName(""));
+ assertEquals(null, ShortcutInfo.getResourcePackageName("abc"));
+ assertEquals("p", ShortcutInfo.getResourcePackageName("p:"));
+ assertEquals("p", ShortcutInfo.getResourcePackageName("p:xx"));
+ assertEquals("pac", ShortcutInfo.getResourcePackageName("pac:"));
+ }
+
+ // Test for a ShortcutInfo method.
+ public void testGetResourceTypeName() {
+ assertEquals(null, ShortcutInfo.getResourceTypeName(""));
+ assertEquals(null, ShortcutInfo.getResourceTypeName(":"));
+ assertEquals(null, ShortcutInfo.getResourceTypeName("/"));
+ assertEquals(null, ShortcutInfo.getResourceTypeName("/:"));
+ assertEquals("a", ShortcutInfo.getResourceTypeName(":a/"));
+ assertEquals("type", ShortcutInfo.getResourceTypeName("xxx:type/yyy"));
+ }
+
+ // Test for a ShortcutInfo method.
+ public void testGetResourceTypeAndEntryName() {
+ assertEquals(null, ShortcutInfo.getResourceTypeAndEntryName(""));
+ assertEquals(null, ShortcutInfo.getResourceTypeAndEntryName("abc"));
+ assertEquals("", ShortcutInfo.getResourceTypeAndEntryName("p:"));
+ assertEquals("x", ShortcutInfo.getResourceTypeAndEntryName(":x"));
+ assertEquals("x", ShortcutInfo.getResourceTypeAndEntryName("p:x"));
+ assertEquals("xyz", ShortcutInfo.getResourceTypeAndEntryName("pac:xyz"));
+ }
+
+ // Test for a ShortcutInfo method.
+ public void testGetResourceEntryName() {
+ assertEquals(null, ShortcutInfo.getResourceEntryName(""));
+ assertEquals(null, ShortcutInfo.getResourceEntryName("ab:"));
+ assertEquals("", ShortcutInfo.getResourceEntryName("/"));
+ assertEquals("abc", ShortcutInfo.getResourceEntryName("/abc"));
+ assertEquals("abc", ShortcutInfo.getResourceEntryName("xyz/abc"));
+ }
+
+ // Test for a ShortcutInfo method.
+ public void testLookUpResourceName_systemResources() {
+ // For android system resources, lookUpResourceName will simply return the value as a
+ // string, regardless of "withType".
+ final Resources res = getTestContext().getResources();
+
+ assertEquals("" + android.R.string.cancel, ShortcutInfo.lookUpResourceName(res,
+ android.R.string.cancel, true, getTestContext().getPackageName()));
+ assertEquals("" + android.R.drawable.alert_dark_frame, ShortcutInfo.lookUpResourceName(res,
+ android.R.drawable.alert_dark_frame, true, getTestContext().getPackageName()));
+ assertEquals("" + android.R.string.cancel, ShortcutInfo.lookUpResourceName(res,
+ android.R.string.cancel, false, getTestContext().getPackageName()));
+ }
+
+ public void testLookUpResourceName_appResources() {
+ final Resources res = getTestContext().getResources();
+
+ assertEquals("shortcut_text1", ShortcutInfo.lookUpResourceName(res,
+ R.string.shortcut_text1, false, getTestContext().getPackageName()));
+ assertEquals("string/shortcut_text1", ShortcutInfo.lookUpResourceName(res,
+ R.string.shortcut_text1, true, getTestContext().getPackageName()));
+
+ assertEquals("black_16x64", ShortcutInfo.lookUpResourceName(res,
+ R.drawable.black_16x64, false, getTestContext().getPackageName()));
+ assertEquals("drawable/black_16x64", ShortcutInfo.lookUpResourceName(res,
+ R.drawable.black_16x64, true, getTestContext().getPackageName()));
+ }
+
+ // Test for a ShortcutInfo method.
+ public void testLookUpResourceId_systemResources() {
+ final Resources res = getTestContext().getResources();
+
+ assertEquals(android.R.string.cancel, ShortcutInfo.lookUpResourceId(res,
+ "" + android.R.string.cancel, null,
+ getTestContext().getPackageName()));
+ assertEquals(android.R.drawable.alert_dark_frame, ShortcutInfo.lookUpResourceId(res,
+ "" + android.R.drawable.alert_dark_frame, null,
+ getTestContext().getPackageName()));
+ }
+
+ // Test for a ShortcutInfo method.
+ public void testLookUpResourceId_appResources() {
+ final Resources res = getTestContext().getResources();
+
+ assertEquals(R.string.shortcut_text1,
+ ShortcutInfo.lookUpResourceId(res, "shortcut_text1", "string",
+ getTestContext().getPackageName()));
+
+ assertEquals(R.string.shortcut_text1,
+ ShortcutInfo.lookUpResourceId(res, "string/shortcut_text1", null,
+ getTestContext().getPackageName()));
+
+ assertEquals(R.drawable.black_16x64,
+ ShortcutInfo.lookUpResourceId(res, "black_16x64", "drawable",
+ getTestContext().getPackageName()));
+
+ assertEquals(R.drawable.black_16x64,
+ ShortcutInfo.lookUpResourceId(res, "drawable/black_16x64", null,
+ getTestContext().getPackageName()));
+ }
+
+ public void testDumpCheckin() throws IOException {
+ prepareCrossProfileDataSet();
+
+ // prepareCrossProfileDataSet() doesn't set any icons, so do set here.
+ final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
+ final Icon res64x64 = Icon.createWithResource(getTestContext(), R.drawable.black_64x64);
+ final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.black_32x32));
+ final Icon bmp64x64 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.black_64x64));
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcutWithIcon("res32x32", res32x32),
+ makeShortcutWithIcon("res64x64", res64x64),
+ makeShortcutWithIcon("bmp32x32", bmp32x32),
+ makeShortcutWithIcon("bmp64x64", bmp64x64))));
+ });
+ // We can't predict the compressed bitmap sizes, so get the real sizes here.
+ final long bitmapTotal =
+ new File(getPackageShortcut(CALLING_PACKAGE_2, "bmp32x32", USER_0)
+ .getBitmapPath()).length() +
+ new File(getPackageShortcut(CALLING_PACKAGE_2, "bmp64x64", USER_0)
+ .getBitmapPath()).length();
+
+ // Read the expected output and inject the bitmap size.
+ final String expected = readTestAsset("shortcut/dumpsys_expected.txt")
+ .replace("***BITMAP_SIZE***", String.valueOf(bitmapTotal));
+
+ assertEquals(expected, dumpCheckin());
+ }
+
+ public void testDumpsysNoPermission() {
+ assertExpectException(SecurityException.class, "android.permission.DUMP",
+ () -> mService.dump(null, new PrintWriter(new StringWriter()), null));
+
+ // System can call it without the permission.
+ runWithSystemUid(() -> {
+ mService.dump(null, new PrintWriter(new StringWriter()), null);
+ });
+ }
+
+ /**
+ * Make sure the legacy file format that only supported a single intent per shortcut
+ * can still be read.
+ */
+ public void testLoadLegacySavedFile() throws Exception {
+ final File path = mService.getUserFile(USER_0);
+ path.getParentFile().mkdirs();
+ try (Writer w = new FileWriter(path)) {
+ w.write(readTestAsset("shortcut/shortcut_legacy_file.xml"));
+ };
+ initService();
+ mService.handleUnlockUser(USER_0);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("manifest-shortcut-storage")
+ .forShortcutWithId("manifest-shortcut-storage", si -> {
+ assertEquals("android.settings.INTERNAL_STORAGE_SETTINGS",
+ si.getIntent().getAction());
+ assertEquals(12345, si.getIntent().getIntExtra("key", 0));
+ });
+ });
+ }
+
+ public void testIsUserUnlocked() {
+ mRunningUsers.clear();
+ mUnlockedUsers.clear();
+
+ assertFalse(mService.isUserUnlockedL(USER_0));
+ assertFalse(mService.isUserUnlockedL(USER_10));
+
+ // Start user 0, still locked.
+ mRunningUsers.put(USER_0, true);
+ assertFalse(mService.isUserUnlockedL(USER_0));
+ assertFalse(mService.isUserUnlockedL(USER_10));
+
+ // Unlock user.
+ mUnlockedUsers.put(USER_0, true);
+ assertTrue(mService.isUserUnlockedL(USER_0));
+ assertFalse(mService.isUserUnlockedL(USER_10));
+
+ // Clear again.
+ mRunningUsers.clear();
+ mUnlockedUsers.clear();
+
+ // Directly call the lifecycle event. Now also locked.
+ mService.handleUnlockUser(USER_0);
+ assertTrue(mService.isUserUnlockedL(USER_0));
+ assertFalse(mService.isUserUnlockedL(USER_10));
+
+ // Directly call the stop lifecycle event. Goes back to the initial state.
+ mService.handleCleanupUser(USER_0);
+ assertFalse(mService.isUserUnlockedL(USER_0));
+ assertFalse(mService.isUserUnlockedL(USER_10));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java
new file mode 100644
index 0000000..eb4db7a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java
@@ -0,0 +1,505 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+
+import android.content.ComponentName;
+import android.content.pm.ShortcutInfo;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.frameworks.servicestests.R;
+import com.android.server.pm.ShortcutService.ConfigConstants;
+
+/**
+ * Tests related to shortcut rank auto-adjustment.
+ */
+@SmallTest
+public class ShortcutManagerTest3 extends BaseShortcutManagerTest {
+
+ private static final String CALLING_PACKAGE = CALLING_PACKAGE_1;
+
+ private static final ComponentName A1 = new ComponentName(CALLING_PACKAGE,
+ ShortcutActivity.class.getName());
+
+ private static final ComponentName A2 = new ComponentName(CALLING_PACKAGE,
+ ShortcutActivity2.class.getName());
+
+ private static final ComponentName A3 = new ComponentName(CALLING_PACKAGE,
+ ShortcutActivity3.class.getName());
+
+ private ShortcutInfo shortcut(String id, ComponentName activity, int rank) {
+ return makeShortcutWithActivityAndRank(id, activity, rank);
+ }
+
+ private ShortcutInfo shortcut(String id, ComponentName activity) {
+ return makeShortcutWithActivityAndRank(id, activity, ShortcutInfo.RANK_NOT_SET);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ // We don't need throttling during this test class, and also relax the max cap.
+ mService.updateConfigurationLocked(
+ ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL + "=99999999,"
+ + ConfigConstants.KEY_MAX_SHORTCUTS + "=99999999"
+ );
+
+ setCaller(CALLING_PACKAGE, USER_0);
+ }
+
+ private void publishManifestShortcuts(ComponentName activity, int resId) {
+ addManifestShortcutResource(activity, resId);
+ updatePackageVersion(CALLING_PACKAGE, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE, USER_0));
+ }
+
+ public void testSetDynamicShortcuts_noManifestShortcuts() {
+ mManager.setDynamicShortcuts(list(
+ shortcut("s1", A1)
+ ));
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s1");
+
+ assertTrue(mManager.setDynamicShortcuts(list(
+ shortcut("s5", A1),
+ shortcut("s4", A1),
+ shortcut("s3", A1)
+ )));
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s5", "s4", "s3");
+
+ // RANK_NOT_SET is always the last.
+ assertTrue(mManager.setDynamicShortcuts(list(
+ shortcut("s5", A1),
+ shortcut("s4", A1, 5),
+ shortcut("s3", A1, 3),
+ shortcut("s2", A1)
+ )));
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s3", "s4", "s5", "s2");
+
+ // Same rank, preserve the argument order.
+ assertTrue(mManager.setDynamicShortcuts(list(
+ shortcut("s5", A1, 5),
+ shortcut("s4", A1, 0),
+ shortcut("s3", A1, 5)
+ )));
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s4", "s5", "s3");
+
+ // Multiple activities.
+ assertTrue(mManager.setDynamicShortcuts(list(
+ shortcut("s5", A1),
+ shortcut("s4", A2),
+ shortcut("s3", A3)
+ )));
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s5");
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("s4");
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A3)
+ .haveRanksInOrder("s3");
+
+ assertTrue(mManager.setDynamicShortcuts(list(
+ shortcut("s5", A1, 5),
+ shortcut("s4", A1),
+ shortcut("s3", A1, 5),
+ shortcut("x5", A2, 5),
+ shortcut("x4", A2),
+ shortcut("x3", A2, 1)
+ )));
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s5", "s3", "s4");
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("x3", "x5", "x4");
+
+ // Clear. Make sure it wouldn't lead to invalid internals state.
+ // (ShortcutService.verifyStates() will do so internally.)
+ assertTrue(mManager.setDynamicShortcuts(list()));
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1).isEmpty();
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2).isEmpty();
+ }
+
+ private void runTestWithManifestShortcuts(Runnable r) {
+ publishManifestShortcuts(A1, R.xml.shortcut_5_alt);
+ publishManifestShortcuts(A2, R.xml.shortcut_1);
+
+ assertWith(getCallerShortcuts()).selectManifest().selectByActivity(A1)
+ .haveRanksInOrder("ms1_alt", "ms2_alt", "ms3_alt", "ms4_alt", "ms5_alt");
+
+ assertWith(getCallerShortcuts()).selectManifest().selectByActivity(A2)
+ .haveRanksInOrder("ms1");
+
+ // Existence of manifest shortcuts shouldn't affect dynamic shortcut ranks,
+ // so running another test here should pass.
+ r.run();
+
+ // And dynamic shortcut tests shouldn't affect manifest shortcuts, so repeat the
+ // same check.
+ assertWith(getCallerShortcuts()).selectManifest().selectByActivity(A1)
+ .haveRanksInOrder("ms1_alt", "ms2_alt", "ms3_alt", "ms4_alt", "ms5_alt");
+
+ assertWith(getCallerShortcuts()).selectManifest().selectByActivity(A2)
+ .haveRanksInOrder("ms1");
+ }
+
+ public void testSetDynamicShortcuts_withManifestShortcuts() {
+ runTestWithManifestShortcuts(() -> testSetDynamicShortcuts_noManifestShortcuts());
+ }
+
+ public void testAddDynamicShortcuts_noManifestShortcuts() {
+ mManager.addDynamicShortcuts(list(
+ shortcut("s1", A1)
+ ));
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s1");
+
+ //------------------------------------------------------
+ long lastApiTime = ++mInjectedCurrentTimeMillis;
+
+ mManager.addDynamicShortcuts(list(
+ shortcut("s5", A1, 0),
+ shortcut("s4", A1),
+ shortcut("s2", A1, 3),
+ shortcut("x1", A2),
+ shortcut("x3", A2, 2),
+ shortcut("x2", A2, 2),
+ shortcut("s3", A1, 0)
+ ));
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s5", "s3", "s1", "s2", "s4");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("x3", "x2", "x1");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByChangedSince(lastApiTime)
+ .haveIds("s5", "s3", "s1", "s2", "s4", "x3", "x2", "x1");
+
+ //------------------------------------------------------
+ lastApiTime = ++mInjectedCurrentTimeMillis;
+
+ mManager.addDynamicShortcuts(list(
+ shortcut("s1", A1, 1)
+ ));
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s5", "s1", "s3", "s2", "s4");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("x3", "x2", "x1");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByChangedSince(lastApiTime)
+ .haveIds("s1", "s3");
+
+ //------------------------------------------------------
+ lastApiTime = ++mInjectedCurrentTimeMillis;
+
+ mManager.addDynamicShortcuts(list(
+ shortcut("s1", A1, 1),
+
+ // This is add, not update, so the following means s5 will have NO_RANK,
+ // which puts it at the end.
+ shortcut("s5", A1),
+ shortcut("s3", A1, 0),
+
+ // s10 also has NO_RANK, so it'll be put at the end, even after "s5" as we preserve
+ // the argument order.
+ shortcut("s10", A1),
+
+ // Note we're changing the activity for x2.
+ shortcut("x2", A1, 0),
+ shortcut("x10", A2)
+ ));
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s3", "x2", "s1", "s2", "s4", "s5", "s10");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("x3", "x1", "x10");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByChangedSince(lastApiTime)
+ .haveIds("s3", "x2", "s1", "s5", "s10", "x1", "x10");
+
+ //------------------------------------------------------
+ lastApiTime = ++mInjectedCurrentTimeMillis;
+
+ // Change the activities again.
+ mManager.addDynamicShortcuts(list(
+ shortcut("s1", A2),
+ shortcut("s2", A2, 999)
+ ));
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s3", "x2", "s4", "s5", "s10");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("x3", "x1", "x10", "s2", "s1");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByChangedSince(lastApiTime)
+ .haveIds("s1", "s2", "s4", "s5", "s10");
+ }
+
+ public void testAddDynamicShortcuts_withManifestShortcuts() {
+ runTestWithManifestShortcuts(() -> testAddDynamicShortcuts_noManifestShortcuts());
+ }
+
+ public void testUpdateShortcuts_noManifestShortcuts() {
+ mManager.addDynamicShortcuts(list(
+ shortcut("s5", A1, 0),
+ shortcut("s4", A1),
+ shortcut("s2", A1, 3),
+ shortcut("x1", A2),
+ shortcut("x3", A2, 2),
+ shortcut("x2", A2, 2),
+ shortcut("s3", A1, 0)
+ ));
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s5", "s3", "s2", "s4");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("x3", "x2", "x1");
+
+ //------------------------------------------------------
+ long lastApiTime = ++mInjectedCurrentTimeMillis;
+
+ mManager.updateShortcuts(list());
+ // Same order.
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s5", "s3", "s2", "s4");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("x3", "x2", "x1");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByChangedSince(lastApiTime)
+ .isEmpty();
+
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE, list("s2", "s4", "x2"), HANDLE_USER_0);
+ });
+ // Still same order.
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s5", "s3", "s2", "s4");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("x3", "x2", "x1");
+
+ //------------------------------------------------------
+ lastApiTime = ++mInjectedCurrentTimeMillis;
+
+ mManager.updateShortcuts(list(
+ shortcut("s4", A1, 1),
+
+ // Rank not changing, should keep the same positions.
+ // c.f. in case of addDynamicShortcuts, this means "put them at the end".
+ shortcut("s3", A1),
+ shortcut("x2", A2)
+ ));
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s5", "s4", "s3", "s2");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("x3", "x2", "x1");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByChangedSince(lastApiTime)
+ .haveIds("s4", "s3", "s2", "x2");
+
+ //------------------------------------------------------
+ lastApiTime = ++mInjectedCurrentTimeMillis;
+
+ mManager.updateShortcuts(list(
+ shortcut("s4", A1, 0),
+
+ // Change the activity without specifying a rank -> keep the same rank.
+ shortcut("s5", A2),
+
+ // Change the activity without specifying a rank -> assign a new rank.
+ shortcut("x2", A1, 2),
+
+ // "xx" doesn't exist, so it'll be ignored.
+ shortcut("xx", A1, 0)
+ ));
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s4", "x2", "s3", "s2");
+
+ // Interesting case: both x3 and s5 originally had rank=0, and in this case s5 has moved
+ // to A2 without changing the rank. So they're tie for the new rank, as well as
+ // the "rank changed" bit. Also in this case, "s5" won't have an implicit order, since
+ // its rank isn't changing. So we sort them by ID, thus s5 comes before x3.
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("s5", "x3", "x1");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByChangedSince(lastApiTime)
+ .haveIds("s4", "x2", "s5", "x3");
+
+ //------------------------------------------------------
+ lastApiTime = ++mInjectedCurrentTimeMillis;
+
+ mManager.updateShortcuts(list(
+ shortcut("s3", A3)));
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s4", "x2", "s2");
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("s5", "x3", "x1");
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A3)
+ .haveRanksInOrder("s3");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByChangedSince(lastApiTime)
+ .haveIds("s3", "s2");
+ }
+
+ public void testUpdateShortcuts_withManifestShortcuts() {
+ runTestWithManifestShortcuts(() -> testUpdateShortcuts_noManifestShortcuts());
+ }
+
+ public void testDeleteDynamicShortcuts_noManifestShortcuts() {
+ mManager.addDynamicShortcuts(list(
+ shortcut("s5", A1, 0),
+ shortcut("s4", A1),
+ shortcut("s2", A1, 3),
+ shortcut("x1", A2),
+ shortcut("x3", A2, 2),
+ shortcut("x2", A2, 2),
+ shortcut("s3", A1, 0)
+ ));
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s5", "s3", "s2", "s4");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("x3", "x2", "x1");
+
+ //------------------------------------------------------
+ long lastApiTime = ++mInjectedCurrentTimeMillis;
+
+ mManager.removeDynamicShortcuts(list());
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s5", "s3", "s2", "s4");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("x3", "x2", "x1");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByChangedSince(lastApiTime)
+ .isEmpty();
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(
+ CALLING_PACKAGE, list("s2", "s4", "x1", "x2"), HANDLE_USER_0);
+ });
+ // Still same order.
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s5", "s3", "s2", "s4");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("x3", "x2", "x1");
+
+ //------------------------------------------------------
+ lastApiTime = ++mInjectedCurrentTimeMillis;
+
+ mManager.removeDynamicShortcuts(list("s3", "x1", "xxxx"));
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s5", "s2", "s4");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("x3", "x2");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByChangedSince(lastApiTime)
+ .haveIds("s2", "s4");
+ }
+
+ public void testDeleteDynamicShortcuts_withManifestShortcuts() {
+ runTestWithManifestShortcuts(() -> testDeleteDynamicShortcuts_noManifestShortcuts());
+ }
+
+ public void testDisableShortcuts_noManifestShortcuts() {
+ mManager.addDynamicShortcuts(list(
+ shortcut("s5", A1, 0),
+ shortcut("s4", A1),
+ shortcut("s2", A1, 3),
+ shortcut("x1", A2),
+ shortcut("x3", A2, 2),
+ shortcut("x2", A2, 2),
+ shortcut("s3", A1, 0)
+ ));
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s5", "s3", "s2", "s4");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("x3", "x2", "x1");
+
+ //------------------------------------------------------
+ long lastApiTime = ++mInjectedCurrentTimeMillis;
+
+ mManager.disableShortcuts(list());
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s5", "s3", "s2", "s4");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("x3", "x2", "x1");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByChangedSince(lastApiTime)
+ .isEmpty();
+
+ //------------------------------------------------------
+ lastApiTime = ++mInjectedCurrentTimeMillis;
+
+ mManager.disableShortcuts(list("s3", "x1", "xxxx"));
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s5", "s2", "s4");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("x3", "x2");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByChangedSince(lastApiTime)
+ .haveIds("s2", "s4");
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE, list("s2", "s4", "x2"), HANDLE_USER_0);
+ });
+ // Still same order.
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s5", "s2", "s4");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("x3", "x2");
+
+ //------------------------------------------------------
+ lastApiTime = ++mInjectedCurrentTimeMillis;
+
+ mManager.disableShortcuts(list("s2", "x3"));
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A1)
+ .haveRanksInOrder("s5", "s4");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByActivity(A2)
+ .haveRanksInOrder("x2");
+
+ assertWith(getCallerShortcuts()).selectDynamic().selectByChangedSince(lastApiTime)
+ .haveIds("s4", "x2");
+ }
+
+ public void testDisableShortcuts_withManifestShortcuts() {
+ runTestWithManifestShortcuts(() -> testDisableShortcuts_noManifestShortcuts());
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest4.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest4.java
new file mode 100644
index 0000000..583c3d4
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest4.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertBundlesEqual;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.makeBundle;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.makePersistableBundle;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.test.suitebuilder.annotation.SmallTest;
+
+@SmallTest
+public class ShortcutManagerTest4 extends BaseShortcutManagerTest {
+
+ private static Bundle sIntentExtras = makeBundle(
+ "key{\u0000}", "value{\u0000}",
+ "key{\u0001}", "value{\u0001}",
+ "key{\u001f}", "value{\u001f}",
+ "key{\u007f}", "value{\u007f}",
+
+ "key{\ud800\udc00}", "value{\ud800\udc00}",
+ "key{\ud801\udc01}", "value{\ud801\udc01}",
+ "key{\udbff\udfff}", "value{\udbff\udfff}",
+
+ "key{\ud801}x", 1, // broken surrogate pair
+ "key{\uDC01}\"x", 2, // broken surrogate pair
+
+ "x1", "value{\ud801}x", // broken surrogate pair
+ "x2", "value{\uDC01}\"x" // broken surrogate pair
+ );
+
+ // Same as above, except broken surrogate pairs are replaced with '?'s.
+ private static Bundle sIntentExtrasDecoded = makeBundle(
+ "key{\u0000}", "value{\u0000}",
+ "key{\u0001}", "value{\u0001}",
+ "key{\u001f}", "value{\u001f}",
+ "key{\u007f}", "value{\u007f}",
+
+ "key{\ud800\udc00}", "value{\ud800\udc00}",
+ "key{\ud801\udc01}", "value{\ud801\udc01}",
+ "key{\udbff\udfff}", "value{\udbff\udfff}",
+
+ "key{?}x", 1,
+ "key{?}\"x", 2,
+
+ "x1", "value{?}x",
+ "x2", "value{?}\"x"
+ );
+
+ private static PersistableBundle sShortcutExtras = makePersistableBundle(
+ "key{\u0000}", "value{\u0000}",
+ "key{\u0001}", "value{\u0001}",
+ "key{\u001f}", "value{\u001f}",
+ "key{\u007f}", "value{\u007f}",
+
+ "key{\ud800\udc00}", "value{\ud800\udc00}",
+ "key{\ud801\udc01}", "value{\ud801\udc01}",
+ "key{\udbff\udfff}", "value{\udbff\udfff}",
+
+ "key{\ud801}", 1, // broken surrogate pair
+ "key{\uDC01}", 2, // broken surrogate pair
+
+ "x1", "value{\ud801}", // broken surrogate pair
+ "x2", "value{\uDC01}" // broken surrogate pair
+ );
+
+ // Same as above, except broken surrogate pairs are replaced with '?'s.
+ private static PersistableBundle sShortcutExtrasDecoded = makePersistableBundle(
+ "key{\u0000}", "value{\u0000}",
+ "key{\u0001}", "value{\u0001}",
+ "key{\u001f}", "value{\u001f}",
+ "key{\u007f}", "value{\u007f}",
+
+ "key{\ud800\udc00}", "value{\ud800\udc00}",
+ "key{\ud801\udc01}", "value{\ud801\udc01}",
+ "key{\udbff\udfff}", "value{\udbff\udfff}",
+
+ "key{?}", 1,
+ "key{?}", 2,
+
+ "x1", "value{?}",
+ "x2", "value{?}"
+ );
+
+ public void testPersistingWeirdCharacters() {
+ final Intent intent = new Intent(Intent.ACTION_MAIN)
+ .putExtras(sIntentExtras);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcutWithExtras("s1", intent, sShortcutExtras),
+ makeShortcut("s{\u0000}{\u0001}{\uD800\uDC00}x[\uD801][\uDC01]")
+ )));
+ });
+
+ // Make sure save & load works fine. (i.e. shouldn't crash even with invalid characters.)
+ initService();
+ mService.handleUnlockUser(USER_0);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("s1", "s{\u0000}{\u0001}{\uD800\uDC00}x[?][?]")
+ .forShortcutWithId("s1", si -> {
+ assertBundlesEqual(si.getIntent().getExtras(), sIntentExtrasDecoded);
+ assertBundlesEqual(si.getExtras(), sShortcutExtrasDecoded);
+ });
+ });
+ }
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest5.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest5.java
new file mode 100644
index 0000000..29c98dc
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest5.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.ShortcutServiceInternal;
+import android.content.res.XmlResourceParser;
+import android.os.Looper;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.LocalServices;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Unit tests for all the IPackageManager related methods in {@link ShortcutService}.
+ *
+ * All the tests here actually talks to the real IPackageManager, so we can't test complicated
+ * cases. Instead we just make sure they all work reasonably without at least crashing.
+ */
+@SmallTest
+public class ShortcutManagerTest5 extends BaseShortcutManagerTest {
+ private ShortcutService mShortcutService;
+
+ private String mMyPackage;
+ private int mMyUserId;
+
+ public static class ShortcutEnabled extends Activity {
+ }
+
+ public static class ShortcutDisabled extends Activity {
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
+ mShortcutService = new ShortcutService(getTestContext(), Looper.getMainLooper(),
+ /* onyForPackageManagerApis */ true);
+
+ mMyPackage = getTestContext().getPackageName();
+ mMyUserId = android.os.Process.myUserHandle().getIdentifier();
+ }
+
+ public void testGetPackageUid() {
+ assertTrue(mShortcutService.injectGetPackageUid(
+ mMyPackage, mMyUserId) != 0);
+
+ assertEquals(-1, mShortcutService.injectGetPackageUid(
+ "no.such.package", mMyUserId));
+ }
+
+ public void testGetPackageInfo() {
+ PackageInfo pi = mShortcutService.getPackageInfo(
+ mMyPackage, mMyUserId, /*signature*/ false);
+ assertEquals(mMyPackage, pi.packageName);
+ assertNull(pi.signatures);
+
+ pi = mShortcutService.getPackageInfo(
+ mMyPackage, mMyUserId, /*signature*/ true);
+ assertEquals(mMyPackage, pi.packageName);
+ assertNotNull(pi.signatures);
+
+ pi = mShortcutService.getPackageInfo(
+ "no.such.package", mMyUserId, /*signature*/ true);
+ assertNull(pi);
+ }
+
+ public void testGetApplicationInfo() {
+ ApplicationInfo ai = mShortcutService.getApplicationInfo(
+ mMyPackage, mMyUserId);
+ assertEquals(mMyPackage, ai.packageName);
+
+ ai = mShortcutService.getApplicationInfo(
+ "no.such.package", mMyUserId);
+ assertNull(ai);
+ }
+
+ public void testGetActivityInfoWithMetadata() {
+ // Disabled activity
+ ActivityInfo ai = mShortcutService.getActivityInfoWithMetadata(
+ new ComponentName(mMyPackage, "ShortcutDisabled"), mMyUserId);
+ assertNull(ai);
+
+ // Nonexistent
+ ai = mShortcutService.getActivityInfoWithMetadata(
+ new ComponentName("no.such.package", "ShortcutDisabled"), mMyUserId);
+ assertNull(ai);
+
+ // Existent, with no metadata.
+ ai = mShortcutService.getActivityInfoWithMetadata(
+ new ComponentName(mMyPackage, "a.ShortcutEnabled"), mMyUserId);
+ assertEquals(mMyPackage, ai.packageName);
+ assertEquals("a.ShortcutEnabled", ai.name);
+ assertNull(ai.loadXmlMetaData(getTestContext().getPackageManager(),
+ "android.app.shortcuts"));
+
+ // Existent, with a shortcut metadata.
+ ai = mShortcutService.getActivityInfoWithMetadata(
+ new ComponentName(mMyPackage, "a.Shortcut1"), mMyUserId);
+ assertEquals(mMyPackage, ai.packageName);
+ assertEquals("a.Shortcut1", ai.name);
+ XmlResourceParser meta = ai.loadXmlMetaData(getTestContext().getPackageManager(),
+ "android.app.shortcuts");
+ assertNotNull(meta);
+ meta.close();
+ }
+
+ public void testGetInstalledPackages() {
+ List<PackageInfo> apks = mShortcutService.getInstalledPackages(mMyUserId);
+
+ Set<String> expectedPackages = set("com.android.settings", mMyPackage);
+ for (PackageInfo pi : apks) {
+ expectedPackages.remove(pi.packageName);
+ }
+ assertEquals(set(), expectedPackages);
+ }
+
+ public void testGetDefaultMainActivity() {
+ ComponentName cn = mShortcutService.injectGetDefaultMainActivity(
+ "com.android.settings", mMyUserId);
+
+ assertEquals(
+ ComponentName.unflattenFromString("com.android.settings/.Settings"),
+ cn);
+
+ // This package has no main activity.
+ assertNull(mShortcutService.injectGetDefaultMainActivity(
+ mMyPackage, mMyUserId));
+
+ // Nonexistent.
+ assertNull(mShortcutService.injectGetDefaultMainActivity(
+ "no.such.package", mMyUserId));
+ }
+
+ public void testIsMainActivity() {
+ assertTrue(mShortcutService.injectIsMainActivity(
+ ComponentName.unflattenFromString("com.android.settings/.Settings"), mMyUserId));
+ assertFalse(mShortcutService.injectIsMainActivity(
+ ComponentName.unflattenFromString("com.android.settings/.xxx"), mMyUserId));
+ assertFalse(mShortcutService.injectIsMainActivity(
+ ComponentName.unflattenFromString("no.such.package/.xxx"), mMyUserId));
+
+ assertFalse(mShortcutService.injectIsMainActivity(
+ new ComponentName(mMyPackage, "a.DisabledMain"), mMyUserId));
+ assertFalse(mShortcutService.injectIsMainActivity(
+ new ComponentName(mMyPackage, "a.UnexportedMain"), mMyUserId));
+
+ }
+
+ public void testGetMainActivities() {
+ assertEquals(1, mShortcutService.injectGetMainActivities(
+ "com.android.settings", mMyUserId).size());
+
+ // This APK has no main activities.
+ assertEquals(0, mShortcutService.injectGetMainActivities(
+ mMyPackage, mMyUserId).size());
+ }
+
+ public void testIsActivityEnabledAndExported() {
+ assertTrue(mShortcutService.injectIsActivityEnabledAndExported(
+ ComponentName.unflattenFromString("com.android.settings/.Settings"), mMyUserId));
+ assertFalse(mShortcutService.injectIsActivityEnabledAndExported(
+ ComponentName.unflattenFromString("com.android.settings/.xxx"), mMyUserId));
+ assertFalse(mShortcutService.injectIsActivityEnabledAndExported(
+ ComponentName.unflattenFromString("no.such.package/.xxx"), mMyUserId));
+
+ assertTrue(mShortcutService.injectIsActivityEnabledAndExported(
+ new ComponentName(mMyPackage, "com.android.server.pm.ShortcutTestActivity"),
+ mMyUserId));
+
+ assertTrue(mShortcutService.injectIsActivityEnabledAndExported(
+ new ComponentName(mMyPackage, "a.ShortcutEnabled"), mMyUserId));
+
+ assertFalse(mShortcutService.injectIsActivityEnabledAndExported(
+ new ComponentName(mMyPackage, "a.ShortcutDisabled"), mMyUserId));
+ assertFalse(mShortcutService.injectIsActivityEnabledAndExported(
+ new ComponentName(mMyPackage, "a.ShortcutUnexported"), mMyUserId));
+
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest6.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest6.java
new file mode 100644
index 0000000..ba4dbc1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest6.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.List;
+
+/**
+ * Tests for {@link ShortcutService#hasShortcutHostPermissionInner}.
+ */
+@SmallTest
+public class ShortcutManagerTest6 extends BaseShortcutManagerTest {
+ public void testHasShortcutHostPermissionInner_systemLauncherOnly() {
+ // Preferred isn't set, use the system launcher.
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ null,
+ list(getSystemLauncher(), getFallbackLauncher()),
+ USER_0);
+ assertTrue(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(PACKAGE_FALLBACK_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_1, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_2, USER_0));
+
+ // Should be cached.
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getLastKnownLauncher());
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+
+ // Also make sure the last known is saved, but the cached is not.
+
+ initService();
+
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getLastKnownLauncher());
+ assertEquals(null,
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+ }
+
+ public void testHasShortcutHostPermissionInner_with3pLauncher() {
+ // Preferred isn't set, still use the system launcher.
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ null,
+ list(getSystemLauncher(), getFallbackLauncher(),
+ ri(CALLING_PACKAGE_1, "name", false, 0),
+ ri(CALLING_PACKAGE_2, "name", false, 0)
+ ),
+ USER_0);
+ assertTrue(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(PACKAGE_FALLBACK_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_1, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_2, USER_0));
+
+ // Should be cached.
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getLastKnownLauncher());
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+ }
+
+ public void testHasShortcutHostPermissionInner_with3pLauncher_complicated() {
+ // Preferred is set. That's the default launcher.
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ cn(CALLING_PACKAGE_2, "name"),
+ list(getSystemLauncher(), getFallbackLauncher(),
+ ri(CALLING_PACKAGE_1, "name", false, 0),
+ ri(CALLING_PACKAGE_2, "name", false, 0)
+ ),
+ USER_0);
+ assertFalse(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(PACKAGE_FALLBACK_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_1, USER_0));
+ assertTrue(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_2, USER_0));
+
+ // Should be cached.
+ assertEquals(cn(CALLING_PACKAGE_2, "name"),
+ mService.getUserShortcutsLocked(USER_0).getLastKnownLauncher());
+ assertEquals(cn(CALLING_PACKAGE_2, "name"),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+
+
+ // Once set, even after the preferred launcher is cleared, SM still allows it to access
+ // shortcuts.
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ null,
+ list(getSystemLauncher(), getFallbackLauncher(),
+ ri(CALLING_PACKAGE_1, "name", false, 0),
+ ri(CALLING_PACKAGE_2, "name", false, 0)
+ ),
+ USER_0);
+
+ assertFalse(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(PACKAGE_FALLBACK_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_1, USER_0));
+ assertTrue(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_2, USER_0));
+
+ // Should be cached.
+ assertEquals(cn(CALLING_PACKAGE_2, "name"),
+ mService.getUserShortcutsLocked(USER_0).getLastKnownLauncher());
+ assertEquals(cn(CALLING_PACKAGE_2, "name"),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+
+ // However, if the component has been disabled, then we'll recalculate it.
+ mEnabledActivityChecker = (comp, user) -> false;
+
+ assertTrue(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(PACKAGE_FALLBACK_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_1, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_2, USER_0));
+
+ mEnabledActivityChecker = (comp, user) -> true;
+
+ // Now the preferred changed.
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ cn(CALLING_PACKAGE_1, "xyz"),
+ list(getSystemLauncher(), getFallbackLauncher(),
+ ri(CALLING_PACKAGE_1, "name", false, 0),
+ ri(CALLING_PACKAGE_2, "name", false, 0)
+ ),
+ USER_0);
+
+ assertTrue(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_1, USER_0));
+
+ // Should be cached.
+ assertEquals(cn(CALLING_PACKAGE_1, "xyz"),
+ mService.getUserShortcutsLocked(USER_0).getLastKnownLauncher());
+ assertEquals(cn(CALLING_PACKAGE_1, "xyz"),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+
+
+ // As long as there's the cached launcher set, even if getHomeActivitiesAsUser()
+ // returns different values, the cached one is still the default.
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ getSystemLauncher().activityInfo.getComponentName(),
+ list(getSystemLauncher(), getFallbackLauncher()),
+ USER_0);
+
+ assertTrue(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_1, USER_0));
+
+ // Cached ones haven't changed.
+ assertEquals(cn(CALLING_PACKAGE_1, "xyz"),
+ mService.getUserShortcutsLocked(USER_0).getLastKnownLauncher());
+ assertEquals(cn(CALLING_PACKAGE_1, "xyz"),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+
+ // However, now the "real" default launcher is the system one. So if the system
+ // launcher asks for shortcuts, we'll allow it.
+ assertTrue(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_0));
+
+ // Since the cache is updated, CALLING_PACKAGE_1 no longer has the permission.
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_1, USER_0));
+
+ // Cached ones haven't changed.
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getLastKnownLauncher());
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+ }
+
+ public void testHasShortcutHostPermissionInner_multiUser() {
+ mRunningUsers.put(USER_10, true);
+
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ null,
+ list(getSystemLauncher(), getFallbackLauncher()),
+ USER_0);
+
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ cn(CALLING_PACKAGE_2, "name"),
+ list(getSystemLauncher(), getFallbackLauncher(),
+ ri(CALLING_PACKAGE_1, "name", false, 0),
+ ri(CALLING_PACKAGE_2, "name", false, 0)
+ ),
+ USER_10);
+
+ assertTrue(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(PACKAGE_FALLBACK_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_1, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_2, USER_0));
+
+ // Check the cache.
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getLastKnownLauncher());
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+
+ assertFalse(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_10));
+ assertFalse(mService.hasShortcutHostPermissionInner(PACKAGE_FALLBACK_LAUNCHER, USER_10));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_1, USER_10));
+ assertTrue(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_2, USER_10));
+
+ // Check the cache.
+ assertEquals(cn(CALLING_PACKAGE_2, "name"),
+ mService.getUserShortcutsLocked(USER_10).getLastKnownLauncher());
+ assertEquals(cn(CALLING_PACKAGE_2, "name"),
+ mService.getUserShortcutsLocked(USER_10).getCachedLauncher());
+ }
+
+ public void testHasShortcutHostPermissionInner_clearCache() {
+ mRunningUsers.put(USER_10, true);
+
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ null,
+ list(getSystemLauncher(), getFallbackLauncher()),
+ USER_0);
+
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ cn(CALLING_PACKAGE_2, "name"),
+ list(getSystemLauncher(), getFallbackLauncher(),
+ ri(CALLING_PACKAGE_1, "name", false, 0),
+ ri(CALLING_PACKAGE_2, "name", false, 0)
+ ),
+ USER_10);
+
+ assertTrue(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_0));
+ assertTrue(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_2, USER_10));
+
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+
+ assertEquals(cn(CALLING_PACKAGE_2, "name"),
+ mService.getUserShortcutsLocked(USER_10).getCachedLauncher());
+
+ // Test it on a non-running user.
+ // Send ACTION_PREFERRED_ACTIVITY_CHANGED on user 10.
+ // But the user is not running, so will be ignored.
+ mRunningUsers.put(USER_10, false);
+
+ mService.mPackageMonitor.onReceive(mServiceContext,
+ new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED).putExtra(
+ Intent.EXTRA_USER_HANDLE, USER_10));
+
+ // Need to run the user again to access the internal status.
+ mRunningUsers.put(USER_10, true);
+
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+
+ assertEquals(cn(CALLING_PACKAGE_2, "name"),
+ mService.getUserShortcutsLocked(USER_10).getCachedLauncher());
+
+ // Send it again after starting the user.
+ mRunningUsers.put(USER_10, true);
+ mService.mPackageMonitor.onReceive(mServiceContext,
+ new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED).putExtra(
+ Intent.EXTRA_USER_HANDLE, USER_10));
+
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+
+ // Only user-10's cache is cleared.
+ assertEquals(null,
+ mService.getUserShortcutsLocked(USER_10).getCachedLauncher());
+
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
new file mode 100644
index 0000000..3c99174
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertContains;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertSuccess;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.readAll;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.resultContains;
+
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.frameworks.servicestests.R;
+import com.android.server.pm.ShortcutService.ConfigConstants;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Unit test for "cmd shortcut"
+ *
+ * Launcher related commands are tested in
+ */
+@SmallTest
+public class ShortcutManagerTest7 extends BaseShortcutManagerTest {
+ private List<String> callShellCommand(String... args) throws IOException, RemoteException {
+
+ // For reset to work, the current time needs to be incrementing.
+ mInjectedCurrentTimeMillis++;
+
+ final AtomicInteger resultCode = new AtomicInteger(Integer.MIN_VALUE);
+
+ final ResultReceiver rr = new ResultReceiver(mHandler) {
+ @Override
+ public void send(int resultCode_, Bundle resultData) {
+ resultCode.set(resultCode_);
+ }
+ };
+ final File out = File.createTempFile("shellout-", ".tmp",
+ getTestContext().getCacheDir());
+ try {
+ try (final ParcelFileDescriptor fd = ParcelFileDescriptor.open(out,
+ ParcelFileDescriptor.MODE_READ_WRITE)) {
+ mService.onShellCommand(
+ /* fdin*/ null,
+ /* fdout*/ fd.getFileDescriptor(),
+ /* fderr*/ fd.getFileDescriptor(),
+ args, rr);
+ }
+ return readAll(out);
+ } finally {
+ out.delete();
+ }
+ }
+
+ public void testNonShell() throws Exception {
+ mService.mMaxUpdatesPerInterval = 99;
+
+ mInjectedCallingUid = 12345;
+ assertExpectException(SecurityException.class, "must be shell",
+ () -> callShellCommand("reset-config"));
+
+ mInjectedCallingUid = Process.SYSTEM_UID;
+ assertExpectException(SecurityException.class, "must be shell",
+ () -> callShellCommand("reset-config"));
+
+ assertEquals(99, mService.mMaxUpdatesPerInterval);
+ }
+
+ public void testRoot() throws Exception {
+ mService.mMaxUpdatesPerInterval = 99;
+
+ mInjectedCallingUid = Process.ROOT_UID;
+ assertSuccess(callShellCommand("reset-config"));
+
+ assertEquals(3, mService.mMaxUpdatesPerInterval);
+ }
+
+ public void testRestConfig() throws Exception {
+ mService.mMaxUpdatesPerInterval = 99;
+
+ mInjectedCallingUid = Process.SHELL_UID;
+ assertSuccess(callShellCommand("reset-config"));
+
+ assertEquals(3, mService.mMaxUpdatesPerInterval);
+ }
+
+ public void testOverrideConfig() throws Exception {
+ mService.mMaxUpdatesPerInterval = 99;
+
+ mInjectedCallingUid = Process.SHELL_UID;
+ assertSuccess(callShellCommand("override-config",
+ ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL + "=1"));
+
+ assertEquals(1, mService.mMaxUpdatesPerInterval);
+ }
+
+ public void testResetThrottling() throws Exception {
+ prepareCrossProfileDataSet();
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.getRemainingCallCount() < 3);
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.getRemainingCallCount() < 3);
+ });
+
+ mInjectedCallingUid = Process.SHELL_UID;
+ assertSuccess(callShellCommand("reset-throttling"));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.getRemainingCallCount() < 3);
+ });
+ }
+
+ public void testResetThrottling_user_not_running() throws Exception {
+ prepareCrossProfileDataSet();
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.getRemainingCallCount() < 3);
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.getRemainingCallCount() < 3);
+ });
+
+ mInjectedCallingUid = Process.SHELL_UID;
+
+ mRunningUsers.put(USER_10, false);
+
+ assertTrue(resultContains(
+ callShellCommand("reset-throttling", "--user", "10"),
+ "User 10 is not running or locked"));
+
+ mRunningUsers.put(USER_10, true);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.getRemainingCallCount() < 3);
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.getRemainingCallCount() < 3);
+ });
+ }
+
+ public void testResetThrottling_user_running() throws Exception {
+ prepareCrossProfileDataSet();
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.getRemainingCallCount() < 3);
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.getRemainingCallCount() < 3);
+ });
+
+ mRunningUsers.put(USER_10, true);
+ mUnlockedUsers.put(USER_10, true);
+
+ mInjectedCallingUid = Process.SHELL_UID;
+ assertSuccess(callShellCommand("reset-throttling", "--user", "10"));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.getRemainingCallCount() < 3);
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ }
+
+ public void testResetAllThrottling() throws Exception {
+ prepareCrossProfileDataSet();
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.getRemainingCallCount() < 3);
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.getRemainingCallCount() < 3);
+ });
+
+ mInjectedCallingUid = Process.SHELL_UID;
+ assertSuccess(callShellCommand("reset-all-throttling"));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ }
+
+ public void testLauncherCommands() throws Exception {
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ null,
+ list(getSystemLauncher(), getFallbackLauncher()),
+ USER_0);
+
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ cn(CALLING_PACKAGE_2, "name"),
+ list(getSystemLauncher(), getFallbackLauncher(),
+ ri(CALLING_PACKAGE_1, "name", false, 0),
+ ri(CALLING_PACKAGE_2, "name", false, 0)
+ ),
+ USER_10);
+
+ assertTrue(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_0));
+
+ // First, test "get".
+
+ mRunningUsers.put(USER_10, true);
+ mUnlockedUsers.put(USER_10, true);
+ mInjectedCallingUid = Process.SHELL_UID;
+ assertContains(
+ assertSuccess(callShellCommand("get-default-launcher")),
+ "Launcher: ComponentInfo{com.android.systemlauncher/systemlauncher_name}");
+
+ assertContains(
+ assertSuccess(callShellCommand("get-default-launcher", "--user", "10")),
+ "Launcher: ComponentInfo{com.android.test.2/name}");
+
+ // Next, test "clear".
+ assertSuccess(callShellCommand("clear-default-launcher", "--user", "10"));
+
+ // User-10's launcher should be cleared.
+ assertEquals(null, mService.getUserShortcutsLocked(USER_10).getLastKnownLauncher());
+ assertEquals(null, mService.getUserShortcutsLocked(USER_10).getCachedLauncher());
+
+ // but user'0's shouldn't.
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+
+ // Change user-0's launcher.
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ cn(CALLING_PACKAGE_1, "name"),
+ list(
+ ri(CALLING_PACKAGE_1, "name", false, 0)
+ ),
+ USER_0);
+ assertContains(
+ assertSuccess(callShellCommand("get-default-launcher")),
+ "Launcher: ComponentInfo{com.android.test.1/name}");
+ }
+
+ public void testUnloadUser() throws Exception {
+ prepareCrossProfileDataSet();
+
+ assertNotNull(mService.getShortcutsForTest().get(USER_10));
+
+ mRunningUsers.put(USER_10, true);
+ mUnlockedUsers.put(USER_10, true);
+
+ mInjectedCallingUid = Process.SHELL_UID;
+ assertSuccess(callShellCommand("unload-user", "--user", "10"));
+
+ assertNull(mService.getShortcutsForTest().get(USER_10));
+ }
+
+ public void testClearShortcuts() throws Exception {
+
+ mRunningUsers.put(USER_10, true);
+
+ // Add two manifests and two dynamics.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_10));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.addDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"))));
+ });
+ runWithCaller(LAUNCHER_1, USER_10, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms2", "s2"), HANDLE_USER_10);
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1", "ms2", "s1", "s2")
+ .areAllEnabled()
+
+ .selectPinned()
+ .haveIds("ms2", "s2");
+ });
+
+ // First, call for a different package.
+
+ mRunningUsers.put(USER_10, true);
+ mUnlockedUsers.put(USER_10, true);
+
+ mInjectedCallingUid = Process.SHELL_UID;
+ assertSuccess(callShellCommand("clear-shortcuts", "--user", "10", CALLING_PACKAGE_2));
+
+ // Shouldn't be cleared yet.
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1", "ms2", "s1", "s2")
+ .areAllEnabled()
+
+ .selectPinned()
+ .haveIds("ms2", "s2");
+ });
+
+ mInjectedCallingUid = Process.SHELL_UID;
+ assertSuccess(callShellCommand("clear-shortcuts", "--user", "10", CALLING_PACKAGE_1));
+
+ // Only manifest shortcuts will remain, and are no longer pinned.
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1", "ms2")
+ .areAllEnabled()
+ .areAllNotPinned();
+ });
+ }
+}
diff --git a/telecomm/java/android/telecom/ParcelableCallAnalytics.aidl b/services/tests/servicestests/src/com/android/server/pm/ShortcutTestActivity.java
similarity index 69%
copy from telecomm/java/android/telecom/ParcelableCallAnalytics.aidl
copy to services/tests/servicestests/src/com/android/server/pm/ShortcutTestActivity.java
index b7e78d1..d82b0d5 100644
--- a/telecomm/java/android/telecom/ParcelableCallAnalytics.aidl
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutTestActivity.java
@@ -1,11 +1,11 @@
/*
- * Copyright 2016, The Android Open Source Project
+ * 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
+ * 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,
@@ -13,10 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.server.pm;
-package android.telecom;
+import android.app.Activity;
-/**
- * {@hide}
- */
-parcelable ParcelableCallAnalytics;
+public class ShortcutTestActivity extends Activity {
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 48a11c5..9f77297 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -23,12 +23,14 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.AtomicFile;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
+@SmallTest
public class UserManagerServiceTest extends AndroidTestCase {
private static String[] STRING_ARRAY = new String[] {"<tag", "<![CDATA["};
private File restrictionsFile;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 6c2fcd5..ced4980 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -25,6 +25,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
import com.android.internal.util.ArrayUtils;
@@ -33,6 +34,7 @@
import java.util.List;
/** Test {@link UserManager} functionality. */
+@MediumTest
public class UserManagerTest extends AndroidTestCase {
private static final int REMOVE_CHECK_INTERVAL = 500;
private static final int REMOVE_TIMEOUT = 60 * 1000;
@@ -96,7 +98,7 @@
&& !user.isPrimary()) {
found = true;
Bundle restrictions = mUserManager.getUserRestrictions(user.getUserHandle());
- assertFalse("New user should have DISALLOW_CONFIG_WIFI =false by default",
+ assertTrue("Guest user should have DISALLOW_CONFIG_WIFI=true by default",
restrictions.getBoolean(UserManager.DISALLOW_CONFIG_WIFI));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index 5bdf6f7..11f9ebb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -16,13 +16,13 @@
package com.android.server.pm;
-import android.os.UserHandle;
-import com.android.server.devicepolicy.DpmTestUtils;
-
import android.os.Bundle;
+import android.os.UserHandle;
import android.os.UserManager;
import android.test.AndroidTestCase;
-import android.test.MoreAsserts;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.devicepolicy.DpmTestUtils;
/**
* Tests for {@link com.android.server.pm.UserRestrictionsUtils}.
@@ -35,6 +35,7 @@
-w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
* </pre>
*/
+@SmallTest
public class UserRestrictionsUtilsTest extends AndroidTestCase {
public void testNonNull() {
Bundle out = UserRestrictionsUtils.nonNull(null);
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index 9ccb1a6..0bd014c 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -67,7 +67,7 @@
// Screen on time should not keep progressing with screen is off
assertEquals(aih.getScreenOnTimeLocked(7000), 3000);
assertEquals(aih.getScreenOnTimeLocked(8000), 3000);
- aih.writeElapsedTimeLocked();
+ aih.writeAppIdleDurationsLocked();
// Check if the screen on time is persisted across instantiations
AppIdleHistory aih2 = new AppIdleHistory(mStorageDir, 0);
diff --git a/services/tests/shortcutmanagerutils/Android.mk b/services/tests/shortcutmanagerutils/Android.mk
new file mode 100644
index 0000000..701e058
--- /dev/null
+++ b/services/tests/shortcutmanagerutils/Android.mk
@@ -0,0 +1,31 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ mockito-target
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE := ShortcutManagerTestUtils
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
new file mode 100644
index 0000000..78f95c4
--- /dev/null
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -0,0 +1,1068 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm.shortcutmanagertest;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyList;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.Instrumentation;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.Callback;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.BaseBundle;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.UserHandle;
+import android.test.MoreAsserts;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * Common utility methods for ShortcutManager tests. This is used by both CTS and the unit tests.
+ * Because it's used by CTS too, it can only access the public APIs.
+ */
+public class ShortcutManagerTestUtils {
+ private static final String TAG = "ShortcutManagerUtils";
+
+ private static final boolean ENABLE_DUMPSYS = true; // DO NOT SUBMIT WITH true
+
+ private static final int STANDARD_TIMEOUT_SEC = 5;
+
+ private static final String[] EMPTY_STRINGS = new String[0];
+
+ private ShortcutManagerTestUtils() {
+ }
+
+ public static List<String> readAll(File file) throws FileNotFoundException {
+ return readAll(ParcelFileDescriptor.open(
+ file.getAbsoluteFile(), ParcelFileDescriptor.MODE_READ_ONLY));
+ }
+
+ public static List<String> readAll(ParcelFileDescriptor pfd) {
+ try {
+ try {
+ final ArrayList<String> ret = new ArrayList<>();
+ try (BufferedReader r = new BufferedReader(
+ new FileReader(pfd.getFileDescriptor()))) {
+ String line;
+ while ((line = r.readLine()) != null) {
+ ret.add(line);
+ }
+ r.readLine();
+ }
+ return ret;
+ } finally {
+ pfd.close();
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static String concatResult(List<String> result) {
+ final StringBuilder sb = new StringBuilder();
+ for (String s : result) {
+ sb.append(s);
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
+
+ public static boolean resultContains(List<String> result, String expected) {
+ for (String line : result) {
+ if (line.contains(expected)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static List<String> assertSuccess(List<String> result) {
+ if (!resultContains(result, "Success")) {
+ fail("Command failed. Result was:\n" + concatResult(result));
+ }
+ return result;
+ }
+
+ public static List<String> assertContains(List<String> result, String expected) {
+ if (!resultContains(result, expected)) {
+ fail("Didn't contain expected string=" + expected
+ + "\nActual:\n" + concatResult(result));
+ }
+ return result;
+ }
+
+ private static List<String> runCommand(Instrumentation instrumentation, String command) {
+ return runCommand(instrumentation, command, null);
+ }
+ private static List<String> runCommand(Instrumentation instrumentation, String command,
+ Predicate<List<String>> resultAsserter) {
+ Log.d(TAG, "Running command: " + command);
+ final List<String> result;
+ try {
+ result = readAll(
+ instrumentation.getUiAutomation().executeShellCommand(command));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ if (resultAsserter != null && !resultAsserter.test(result)) {
+ fail("Command '" + command + "' failed, output was:\n" + concatResult(result));
+ }
+ return result;
+ }
+
+ private static void runCommandForNoOutput(Instrumentation instrumentation, String command) {
+ runCommand(instrumentation, command, result -> result.size() == 0);
+ }
+
+ private static List<String> runShortcutCommand(Instrumentation instrumentation, String command,
+ Predicate<List<String>> resultAsserter) {
+ return runCommand(instrumentation, "cmd shortcut " + command, resultAsserter);
+ }
+
+ public static List<String> runShortcutCommandForSuccess(Instrumentation instrumentation,
+ String command) {
+ return runShortcutCommand(instrumentation, command, result -> result.contains("Success"));
+ }
+
+ public static String getDefaultLauncher(Instrumentation instrumentation) {
+ final String PREFIX = "Launcher: ComponentInfo{";
+ final String POSTFIX = "}";
+ final List<String> result = runShortcutCommandForSuccess(
+ instrumentation, "get-default-launcher");
+ for (String s : result) {
+ if (s.startsWith(PREFIX) && s.endsWith(POSTFIX)) {
+ return s.substring(PREFIX.length(), s.length() - POSTFIX.length());
+ }
+ }
+ fail("Default launcher not found");
+ return null;
+ }
+
+ public static void setDefaultLauncher(Instrumentation instrumentation, String component) {
+ runCommand(instrumentation, "cmd package set-home-activity " + component,
+ result -> result.contains("Success"));
+ }
+
+ public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) {
+ setDefaultLauncher(instrumentation, packageContext.getPackageName()
+ + "/android.content.pm.cts.shortcutmanager.packages.Launcher");
+ }
+
+ public static void overrideConfig(Instrumentation instrumentation, String config) {
+ runShortcutCommandForSuccess(instrumentation, "override-config " + config);
+ }
+
+ public static void resetConfig(Instrumentation instrumentation) {
+ runShortcutCommandForSuccess(instrumentation, "reset-config");
+ }
+
+ public static void resetThrottling(Instrumentation instrumentation) {
+ runShortcutCommandForSuccess(instrumentation, "reset-throttling");
+ }
+
+ public static void resetAllThrottling(Instrumentation instrumentation) {
+ runShortcutCommandForSuccess(instrumentation, "reset-all-throttling");
+ }
+
+ public static void clearShortcuts(Instrumentation instrumentation, int userId,
+ String packageName) {
+ runShortcutCommandForSuccess(instrumentation, "clear-shortcuts "
+ + " --user " + userId + " " + packageName);
+ }
+
+ public static void anyContains(List<String> result, String expected) {
+ for (String l : result) {
+ if (l.contains(expected)) {
+ return;
+ }
+ }
+ fail("Result didn't contain '" + expected + "': was\n" + result);
+ }
+
+ public static void enableComponent(Instrumentation instrumentation, ComponentName cn,
+ boolean enable) {
+
+ final String word = (enable ? "enable" : "disable");
+ runCommand(instrumentation,
+ "pm " + word + " " + cn.flattenToString()
+ , result ->concatResult(result).contains(word));
+ }
+
+ public static void appOps(Instrumentation instrumentation, String packageName,
+ String op, String mode) {
+ runCommand(instrumentation, "appops set " + packageName + " " + op + " " + mode);
+ }
+
+ public static void dumpsysShortcut(Instrumentation instrumentation) {
+ if (!ENABLE_DUMPSYS) {
+ return;
+ }
+ Log.e(TAG, "Dumpsys shortcut");
+ for (String s : runCommand(instrumentation, "dumpsys shortcut")) {
+ Log.e(TAG, s);
+ }
+ }
+
+ public static JSONObject getCheckinDump(Instrumentation instrumentation) throws JSONException {
+ return new JSONObject(concatResult(runCommand(instrumentation, "dumpsys shortcut -c")));
+ }
+
+ public static boolean isLowRamDevice(Instrumentation instrumentation) throws JSONException {
+ return getCheckinDump(instrumentation).getBoolean("lowRam");
+ }
+
+ public static int getIconSize(Instrumentation instrumentation) throws JSONException {
+ return getCheckinDump(instrumentation).getInt("iconSize");
+ }
+
+ public static Bundle makeBundle(Object... keysAndValues) {
+ assertTrue((keysAndValues.length % 2) == 0);
+
+ if (keysAndValues.length == 0) {
+ return null;
+ }
+ final Bundle ret = new Bundle();
+
+ for (int i = keysAndValues.length - 2; i >= 0; i -= 2) {
+ final String key = keysAndValues[i].toString();
+ final Object value = keysAndValues[i + 1];
+
+ if (value == null) {
+ ret.putString(key, null);
+ } else if (value instanceof Integer) {
+ ret.putInt(key, (Integer) value);
+ } else if (value instanceof String) {
+ ret.putString(key, (String) value);
+ } else if (value instanceof Bundle) {
+ ret.putBundle(key, (Bundle) value);
+ } else {
+ fail("Type not supported yet: " + value.getClass().getName());
+ }
+ }
+ return ret;
+ }
+
+ public static PersistableBundle makePersistableBundle(Object... keysAndValues) {
+ assertTrue((keysAndValues.length % 2) == 0);
+
+ if (keysAndValues.length == 0) {
+ return null;
+ }
+ final PersistableBundle ret = new PersistableBundle();
+
+ for (int i = keysAndValues.length - 2; i >= 0; i -= 2) {
+ final String key = keysAndValues[i].toString();
+ final Object value = keysAndValues[i + 1];
+
+ if (value == null) {
+ ret.putString(key, null);
+ } else if (value instanceof Integer) {
+ ret.putInt(key, (Integer) value);
+ } else if (value instanceof String) {
+ ret.putString(key, (String) value);
+ } else if (value instanceof PersistableBundle) {
+ ret.putPersistableBundle(key, (PersistableBundle) value);
+ } else {
+ fail("Type not supported yet: " + value.getClass().getName());
+ }
+ }
+ return ret;
+ }
+
+ public static <T> List<T> list(T... array) {
+ return Arrays.asList(array);
+ }
+
+ public static <T> Set<T> hashSet(Set<T> in) {
+ return new LinkedHashSet<>(in);
+ }
+
+ public static <T> Set<T> set(T... values) {
+ return set(v -> v, values);
+ }
+
+ public static <T, V> Set<T> set(Function<V, T> converter, V... values) {
+ return set(converter, Arrays.asList(values));
+ }
+
+ public static <T, V> Set<T> set(Function<V, T> converter, List<V> values) {
+ final LinkedHashSet<T> ret = new LinkedHashSet<>();
+ for (V v : values) {
+ ret.add(converter.apply(v));
+ }
+ return ret;
+ }
+
+ public static void resetAll(Collection<?> mocks) {
+ for (Object o : mocks) {
+ reset(o);
+ }
+ }
+
+ public static <T extends Collection<?>> T assertEmpty(T collection) {
+ if (collection == null) {
+ return collection; // okay.
+ }
+ assertEquals(0, collection.size());
+ return collection;
+ }
+
+ public static List<ShortcutInfo> filter(List<ShortcutInfo> list, Predicate<ShortcutInfo> p) {
+ final ArrayList<ShortcutInfo> ret = new ArrayList<>(list);
+ ret.removeIf(si -> !p.test(si));
+ return ret;
+ }
+
+ public static List<ShortcutInfo> filterByActivity(List<ShortcutInfo> list,
+ ComponentName activity) {
+ return filter(list, si ->
+ (si.getActivity().equals(activity)
+ && (si.isDeclaredInManifest() || si.isDynamic())));
+ }
+
+ public static List<ShortcutInfo> changedSince(List<ShortcutInfo> list, long time) {
+ return filter(list, si -> si.getLastChangedTimestamp() >= time);
+ }
+
+ @FunctionalInterface
+ public interface ExceptionRunnable {
+ void run() throws Exception;
+ }
+
+ public static void assertExpectException(Class<? extends Throwable> expectedExceptionType,
+ String expectedExceptionMessageRegex, ExceptionRunnable r) {
+ assertExpectException("", expectedExceptionType, expectedExceptionMessageRegex, r);
+ }
+
+ public static void assertCannotUpdateImmutable(Runnable r) {
+ assertExpectException(
+ IllegalArgumentException.class, "may not be manipulated via APIs", r::run);
+ }
+
+ public static void assertDynamicShortcutCountExceeded(Runnable r) {
+ assertExpectException(IllegalArgumentException.class,
+ "Max number of dynamic shortcuts exceeded", r::run);
+ }
+
+ public static void assertExpectException(String message,
+ Class<? extends Throwable> expectedExceptionType,
+ String expectedExceptionMessageRegex, ExceptionRunnable r) {
+ try {
+ r.run();
+ } catch (Throwable e) {
+ Assert.assertTrue(
+ "Expected exception type was " + expectedExceptionType.getName()
+ + " but caught " + e + " (message=" + message + ")",
+ expectedExceptionType.isAssignableFrom(e.getClass()));
+ if (expectedExceptionMessageRegex != null) {
+ MoreAsserts.assertContainsRegex(expectedExceptionMessageRegex, e.getMessage());
+ }
+ return; // Pass
+ }
+ Assert.fail("Expected exception type " + expectedExceptionType.getName()
+ + " was not thrown");
+ }
+
+ public static List<ShortcutInfo> assertShortcutIds(List<ShortcutInfo> actualShortcuts,
+ String... expectedIds) {
+ final SortedSet<String> expected = new TreeSet<>(list(expectedIds));
+ final SortedSet<String> actual = new TreeSet<>();
+ for (ShortcutInfo s : actualShortcuts) {
+ actual.add(s.getId());
+ }
+
+ // Compare the sets.
+ assertEquals(expected, actual);
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertShortcutIdsOrdered(List<ShortcutInfo> actualShortcuts,
+ String... expectedIds) {
+ final ArrayList<String> expected = new ArrayList<>(list(expectedIds));
+ final ArrayList<String> actual = new ArrayList<>();
+ for (ShortcutInfo s : actualShortcuts) {
+ actual.add(s.getId());
+ }
+ assertEquals(expected, actual);
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllHaveIntents(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertNotNull("ID " + s.getId(), s.getIntent());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllNotHaveIntents(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertNull("ID " + s.getId(), s.getIntent());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllHaveTitle(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertNotNull("ID " + s.getId(), s.getShortLabel());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllNotHaveTitle(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertNull("ID " + s.getId(), s.getShortLabel());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllKeyFieldsOnly(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), s.hasKeyFieldsOnly());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllNotKeyFieldsOnly(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertFalse("ID " + s.getId(), s.hasKeyFieldsOnly());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllDynamic(List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), s.isDynamic());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllPinned(List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), s.isPinned());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllDynamicOrPinned(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), s.isDynamic() || s.isPinned());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllManifest(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), s.isDeclaredInManifest());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllNotManifest(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertFalse("ID " + s.getId(), s.isDeclaredInManifest());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllDisabled(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), !s.isEnabled());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllEnabled(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), s.isEnabled());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllImmutable(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), s.isImmutable());
+ }
+ return actualShortcuts;
+ }
+
+ public static void assertDynamicOnly(ShortcutInfo si) {
+ assertTrue(si.isDynamic());
+ assertFalse(si.isPinned());
+ }
+
+ public static void assertPinnedOnly(ShortcutInfo si) {
+ assertFalse(si.isDynamic());
+ assertFalse(si.isDeclaredInManifest());
+ assertTrue(si.isPinned());
+ }
+
+ public static void assertDynamicAndPinned(ShortcutInfo si) {
+ assertTrue(si.isDynamic());
+ assertTrue(si.isPinned());
+ }
+
+ public static void assertBitmapSize(int expectedWidth, int expectedHeight, Bitmap bitmap) {
+ assertEquals("width", expectedWidth, bitmap.getWidth());
+ assertEquals("height", expectedHeight, bitmap.getHeight());
+ }
+
+ public static <T> void assertAllUnique(Collection<T> list) {
+ final Set<Object> set = new LinkedHashSet<>();
+ for (T item : list) {
+ if (set.contains(item)) {
+ fail("Duplicate item found: " + item + " (in the list: " + list + ")");
+ }
+ set.add(item);
+ }
+ }
+
+ public static ShortcutInfo findShortcut(List<ShortcutInfo> list, String id) {
+ for (ShortcutInfo si : list) {
+ if (si.getId().equals(id)) {
+ return si;
+ }
+ }
+ fail("Shortcut " + id + " not found in the list");
+ return null;
+ }
+
+ public static Bitmap pfdToBitmap(ParcelFileDescriptor pfd) {
+ assertNotNull(pfd);
+ try {
+ try {
+ return BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
+ } finally {
+ pfd.close();
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void assertBundleEmpty(BaseBundle b) {
+ assertTrue(b == null || b.size() == 0);
+ }
+
+ public static void assertCallbackNotReceived(LauncherApps.Callback mock) {
+ verify(mock, times(0)).onShortcutsChanged(anyString(), anyList(),
+ any(UserHandle.class));
+ }
+
+ public static void assertCallbackReceived(LauncherApps.Callback mock,
+ UserHandle user, String packageName, String... ids) {
+ verify(mock).onShortcutsChanged(eq(packageName), checkShortcutIds(ids),
+ eq(user));
+ }
+
+ public static boolean checkAssertSuccess(Runnable r) {
+ try {
+ r.run();
+ return true;
+ } catch (AssertionError e) {
+ return false;
+ }
+ }
+
+ public static <T> T checkArgument(Predicate<T> checker, String description,
+ List<T> matchedCaptor) {
+ final Matcher<T> m = new BaseMatcher<T>() {
+ @Override
+ public boolean matches(Object item) {
+ if (item == null) {
+ return false;
+ }
+ final T value = (T) item;
+ if (!checker.test(value)) {
+ return false;
+ }
+
+ if (matchedCaptor != null) {
+ matchedCaptor.add(value);
+ }
+ return true;
+ }
+
+ @Override
+ public void describeTo(Description d) {
+ d.appendText(description);
+ }
+ };
+ return Mockito.argThat(m);
+ }
+
+ public static List<ShortcutInfo> checkShortcutIds(String... ids) {
+ return checkArgument((List<ShortcutInfo> list) -> {
+ final Set<String> actualSet = set(si -> si.getId(), list);
+ return actualSet.equals(set(ids));
+
+ }, "Shortcut IDs=[" + Arrays.toString(ids) + "]", null);
+ }
+
+ public static ShortcutInfo parceled(ShortcutInfo si) {
+ Parcel p = Parcel.obtain();
+ p.writeParcelable(si, 0);
+ p.setDataPosition(0);
+ ShortcutInfo si2 = p.readParcelable(ShortcutManagerTestUtils.class.getClassLoader());
+ p.recycle();
+ return si2;
+ }
+
+ public static List<ShortcutInfo> cloneShortcutList(List<ShortcutInfo> list) {
+ if (list == null) {
+ return null;
+ }
+ final List<ShortcutInfo> ret = new ArrayList<>(list.size());
+ for (ShortcutInfo si : list) {
+ ret.add(parceled(si));
+ }
+
+ return ret;
+ }
+
+ private static final Comparator<ShortcutInfo> sRankComparator =
+ (ShortcutInfo a, ShortcutInfo b) -> Integer.compare(a.getRank(), b.getRank());
+
+ public static List<ShortcutInfo> sortedByRank(List<ShortcutInfo> shortcuts) {
+ final ArrayList<ShortcutInfo> ret = new ArrayList<>(shortcuts);
+ Collections.sort(ret, sRankComparator);
+ return ret;
+ }
+
+ public static void waitUntil(String message, BooleanSupplier condition) {
+ waitUntil(message, condition, STANDARD_TIMEOUT_SEC);
+ }
+
+ public static void waitUntil(String message, BooleanSupplier condition, int timeoutSeconds) {
+ final long timeout = System.currentTimeMillis() + (timeoutSeconds * 1000L);
+ while (System.currentTimeMillis() < timeout) {
+ if (condition.getAsBoolean()) {
+ return;
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ fail("Timed out for: " + message);
+ }
+
+ public static ShortcutListAsserter assertWith(List<ShortcutInfo> list) {
+ return new ShortcutListAsserter(list);
+ }
+
+ /**
+ * New style assertion that allows chained calls.
+ */
+ public static class ShortcutListAsserter {
+ private final ShortcutListAsserter mOriginal;
+ private final List<ShortcutInfo> mList;
+
+ ShortcutListAsserter(List<ShortcutInfo> list) {
+ this(null, list);
+ }
+
+ private ShortcutListAsserter(ShortcutListAsserter original, List<ShortcutInfo> list) {
+ mOriginal = (original == null) ? this : original;
+ mList = (list == null) ? new ArrayList<>(0) : new ArrayList<>(list);
+ }
+
+ public ShortcutListAsserter revertToOriginalList() {
+ return mOriginal;
+ }
+
+ public ShortcutListAsserter selectDynamic() {
+ return new ShortcutListAsserter(this,
+ filter(mList, ShortcutInfo::isDynamic));
+ }
+
+ public ShortcutListAsserter selectManifest() {
+ return new ShortcutListAsserter(this,
+ filter(mList, ShortcutInfo::isDeclaredInManifest));
+ }
+
+ public ShortcutListAsserter selectPinned() {
+ return new ShortcutListAsserter(this,
+ filter(mList, ShortcutInfo::isPinned));
+ }
+
+ public ShortcutListAsserter selectByActivity(ComponentName activity) {
+ return new ShortcutListAsserter(this,
+ ShortcutManagerTestUtils.filterByActivity(mList, activity));
+ }
+
+ public ShortcutListAsserter selectByChangedSince(long time) {
+ return new ShortcutListAsserter(this,
+ ShortcutManagerTestUtils.changedSince(mList, time));
+ }
+
+ public ShortcutListAsserter selectByIds(String... ids) {
+ final Set<String> idSet = set(ids);
+ final ArrayList<ShortcutInfo> selected = new ArrayList<>();
+ for (ShortcutInfo si : mList) {
+ if (idSet.contains(si.getId())) {
+ selected.add(si);
+ idSet.remove(si.getId());
+ }
+ }
+ if (idSet.size() > 0) {
+ fail("Shortcuts not found for IDs=" + idSet);
+ }
+
+ return new ShortcutListAsserter(this, selected);
+ }
+
+ public ShortcutListAsserter toSortByRank() {
+ return new ShortcutListAsserter(this,
+ ShortcutManagerTestUtils.sortedByRank(mList));
+ }
+
+ public ShortcutListAsserter call(Consumer<List<ShortcutInfo>> c) {
+ c.accept(mList);
+ return this;
+ }
+
+ public ShortcutListAsserter haveIds(String... expectedIds) {
+ assertShortcutIds(mList, expectedIds);
+ return this;
+ }
+
+ public ShortcutListAsserter haveIdsOrdered(String... expectedIds) {
+ assertShortcutIdsOrdered(mList, expectedIds);
+ return this;
+ }
+
+ private ShortcutListAsserter haveSequentialRanks() {
+ for (int i = 0; i < mList.size(); i++) {
+ final ShortcutInfo si = mList.get(i);
+ assertEquals("Rank not sequential: id=" + si.getId(), i, si.getRank());
+ }
+ return this;
+ }
+
+ public ShortcutListAsserter haveRanksInOrder(String... expectedIds) {
+ toSortByRank()
+ .haveSequentialRanks()
+ .haveIdsOrdered(expectedIds);
+ return this;
+ }
+
+ public ShortcutListAsserter isEmpty() {
+ assertEquals(0, mList.size());
+ return this;
+ }
+
+ public ShortcutListAsserter areAllDynamic() {
+ forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isDynamic()));
+ return this;
+ }
+
+ public ShortcutListAsserter areAllNotDynamic() {
+ forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isDynamic()));
+ return this;
+ }
+
+ public ShortcutListAsserter areAllPinned() {
+ forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isPinned()));
+ return this;
+ }
+
+ public ShortcutListAsserter areAllNotPinned() {
+ forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isPinned()));
+ return this;
+ }
+
+ public ShortcutListAsserter areAllManifest() {
+ forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isDeclaredInManifest()));
+ return this;
+ }
+
+ public ShortcutListAsserter areAllNotManifest() {
+ forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isDeclaredInManifest()));
+ return this;
+ }
+
+ public ShortcutListAsserter areAllImmutable() {
+ forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isImmutable()));
+ return this;
+ }
+
+ public ShortcutListAsserter areAllMutable() {
+ forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isImmutable()));
+ return this;
+ }
+
+ public ShortcutListAsserter areAllEnabled() {
+ forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isEnabled()));
+ return this;
+ }
+
+ public ShortcutListAsserter areAllDisabled() {
+ forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isEnabled()));
+ return this;
+ }
+
+ public ShortcutListAsserter areAllWithKeyFieldsOnly() {
+ forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.hasKeyFieldsOnly()));
+ return this;
+ }
+
+ public ShortcutListAsserter areAllNotWithKeyFieldsOnly() {
+ forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.hasKeyFieldsOnly()));
+ return this;
+ }
+
+ public ShortcutListAsserter areAllWithActivity(ComponentName activity) {
+ forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.getActivity().equals(activity)));
+ return this;
+ }
+
+ public ShortcutListAsserter forAllShortcuts(Consumer<ShortcutInfo> sa) {
+ boolean found = false;
+ for (int i = 0; i < mList.size(); i++) {
+ final ShortcutInfo si = mList.get(i);
+ found = true;
+ sa.accept(si);
+ }
+ assertTrue("No shortcuts found.", found);
+ return this;
+ }
+
+ public ShortcutListAsserter forShortcut(Predicate<ShortcutInfo> p,
+ Consumer<ShortcutInfo> sa) {
+ boolean found = false;
+ for (int i = 0; i < mList.size(); i++) {
+ final ShortcutInfo si = mList.get(i);
+ if (p.test(si)) {
+ found = true;
+ try {
+ sa.accept(si);
+ } catch (Throwable e) {
+ throw new AssertionError("Assertion failed for shortcut " + si.getId(), e);
+ }
+ }
+ }
+ assertTrue("Shortcut with the given condition not found.", found);
+ return this;
+ }
+
+ public ShortcutListAsserter forShortcutWithId(String id, Consumer<ShortcutInfo> sa) {
+ forShortcut(si -> si.getId().equals(id), sa);
+
+ return this;
+ }
+ }
+
+ public static void assertBundlesEqual(BaseBundle b1, BaseBundle b2) {
+ if (b1 == null && b2 == null) {
+ return; // pass
+ }
+ assertNotNull("b1 is null but b2 is not", b1);
+ assertNotNull("b2 is null but b1 is not", b2);
+
+ // HashSet makes the error message readable.
+ assertEquals(set(b1.keySet()), set(b2.keySet()));
+
+ for (String key : b1.keySet()) {
+ final Object v1 = b1.get(key);
+ final Object v2 = b2.get(key);
+ if (v1 == null) {
+ if (v2 == null) {
+ return;
+ }
+ }
+ if (v1.equals(v2)) {
+ return;
+ }
+
+ assertTrue("Only either value is null: key=" + key
+ + " b1=" + b1 + " b2=" + b2, v1 != null && v2 != null);
+ assertEquals("Class mismatch: key=" + key, v1.getClass(), v2.getClass());
+
+ if (v1 instanceof BaseBundle) {
+ assertBundlesEqual((BaseBundle) v1, (BaseBundle) v2);
+
+ } else if (v1 instanceof boolean[]) {
+ assertTrue(Arrays.equals((boolean[]) v1, (boolean[]) v2));
+
+ } else if (v1 instanceof int[]) {
+ MoreAsserts.assertEquals((int[]) v1, (int[]) v2);
+
+ } else if (v1 instanceof double[]) {
+ MoreAsserts.assertEquals((double[]) v1, (double[]) v2);
+
+ } else if (v1 instanceof String[]) {
+ MoreAsserts.assertEquals((String[]) v1, (String[]) v2);
+
+ } else if (v1 instanceof Double) {
+ if (((Double) v1).isNaN()) {
+ assertTrue(((Double) v2).isNaN());
+ } else {
+ assertEquals(v1, v2);
+ }
+
+ } else {
+ assertEquals(v1, v2);
+ }
+ }
+ }
+
+ public static void waitOnMainThread() throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ new Handler(Looper.getMainLooper()).post(() -> latch.countDown());
+
+ latch.await();
+ }
+
+ public static class LauncherCallbackAsserter {
+ private final LauncherApps.Callback mCallback = mock(LauncherApps.Callback.class);
+
+ private Callback getMockCallback() {
+ return mCallback;
+ }
+
+ public LauncherCallbackAsserter assertNoCallbackCalled() {
+ verify(mCallback, times(0)).onShortcutsChanged(
+ anyString(),
+ any(List.class),
+ any(UserHandle.class));
+ return this;
+ }
+
+ public LauncherCallbackAsserter assertNoCallbackCalledForPackage(
+ String publisherPackageName) {
+ verify(mCallback, times(0)).onShortcutsChanged(
+ eq(publisherPackageName),
+ any(List.class),
+ any(UserHandle.class));
+ return this;
+ }
+
+ public LauncherCallbackAsserter assertNoCallbackCalledForPackageAndUser(
+ String publisherPackageName, UserHandle publisherUserHandle) {
+ verify(mCallback, times(0)).onShortcutsChanged(
+ eq(publisherPackageName),
+ any(List.class),
+ eq(publisherUserHandle));
+ return this;
+ }
+
+ public ShortcutListAsserter assertCallbackCalledForPackageAndUser(
+ String publisherPackageName, UserHandle publisherUserHandle) {
+ final ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
+ verify(mCallback, times(1)).onShortcutsChanged(
+ eq(publisherPackageName),
+ shortcuts.capture(),
+ eq(publisherUserHandle));
+ return new ShortcutListAsserter(shortcuts.getValue());
+ }
+ }
+
+ public static LauncherCallbackAsserter assertForLauncherCallback(
+ LauncherApps launcherApps, Runnable body) throws InterruptedException {
+ final LauncherCallbackAsserter asserter = new LauncherCallbackAsserter();
+ launcherApps.registerCallback(asserter.getMockCallback(),
+ new Handler(Looper.getMainLooper()));
+
+ body.run();
+
+ waitOnMainThread();
+
+ // TODO unregister doesn't work well during unit tests. Figure out and fix it.
+ // launcherApps.unregisterCallback(asserter.getMockCallback());
+
+ return asserter;
+ }
+
+ public static void retryUntil(BooleanSupplier checker, String message) {
+ final long timeOut = System.currentTimeMillis() + 30 * 1000; // wait for 30 seconds.
+ while (!checker.getAsBoolean()) {
+ if (System.currentTimeMillis() > timeOut) {
+ break;
+ }
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException ignore) {
+ }
+ }
+ assertTrue(message, checker.getAsBoolean());
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index a3313c9..f69dae4 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -118,7 +118,6 @@
} else {
mScreenOnDuration += elapsedRealtime - mScreenOnSnapshot;
mElapsedDuration += elapsedRealtime - mElapsedSnapshot;
- writeScreenOnTimeLocked();
mElapsedSnapshot = elapsedRealtime;
}
}
@@ -167,7 +166,7 @@
/**
* To be called periodically to keep track of elapsed time when app idle times are written
*/
- public void writeElapsedTimeLocked() {
+ public void writeAppIdleDurationsLocked() {
final long elapsedRealtime = SystemClock.elapsedRealtime();
// Only bump up and snapshot the elapsed time. Don't change screen on duration.
mElapsedDuration += elapsedRealtime - mElapsedSnapshot;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index ecfeff9..8284773 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -304,9 +304,9 @@
@Override public void onDisplayChanged(int displayId) {
if (displayId == Display.DEFAULT_DISPLAY) {
+ final boolean displayOn = isDisplayOn();
synchronized (UsageStatsService.this.mLock) {
- mAppIdleHistory.updateDisplayLocked(isDisplayOn(),
- SystemClock.elapsedRealtime());
+ mAppIdleHistory.updateDisplayLocked(displayOn, SystemClock.elapsedRealtime());
}
}
}
@@ -838,6 +838,10 @@
&& mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) {
return false;
}
+
+ if (isDeviceProvisioningPackage(packageName)) {
+ return false;
+ }
}
if (!isAppIdleUnfiltered(packageName, userId, elapsedRealtime)) {
@@ -930,6 +934,16 @@
return dpm.packageHasActiveAdmins(packageName, userId);
}
+ /**
+ * Returns {@code true} if the supplied package is the device provisioning app. Otherwise,
+ * returns {@code false}.
+ */
+ private boolean isDeviceProvisioningPackage(String packageName) {
+ String deviceProvisioningPackage = getContext().getResources().getString(
+ com.android.internal.R.string.config_deviceProvisioningPackage);
+ return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
+ }
+
private boolean isCarrierApp(String packageName) {
synchronized (mLock) {
if (!mHaveCarrierPrivilegedApps) {
@@ -991,9 +1005,9 @@
service.persistActiveStats();
mAppIdleHistory.writeAppIdleTimesLocked(mUserState.keyAt(i));
}
- // Persist elapsed time periodically, in case screen doesn't get toggled
- // until the next boot
- mAppIdleHistory.writeElapsedTimeLocked();
+ // Persist elapsed and screen on time. If this fails for whatever reason, the apps will be
+ // considered not-idle, which is the safest outcome in such an event.
+ mAppIdleHistory.writeAppIdleDurationsLocked();
mHandler.removeMessages(MSG_FLUSH_TO_DISK);
}
@@ -1412,6 +1426,24 @@
}
@Override
+ public void reportShortcutUsage(String packageName, String shortcutId, int userId) {
+ if (packageName == null || shortcutId == null) {
+ Slog.w(TAG, "Event reported without a package name or a shortcut ID");
+ return;
+ }
+
+ UsageEvents.Event event = new UsageEvents.Event();
+ event.mPackage = packageName.intern();
+ event.mShortcutId = shortcutId.intern();
+
+ // This will later be converted to system time.
+ event.mTimeStamp = SystemClock.elapsedRealtime();
+
+ event.mEventType = Event.SHORTCUT_INVOCATION;
+ mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
+ }
+
+ @Override
public void reportContentProviderUsage(String name, String packageName, int userId) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = name;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index c95ff23..03cee9c 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -51,6 +51,7 @@
private static final String ACTIVE_ATTR = "active";
private static final String LAST_EVENT_ATTR = "lastEvent";
private static final String TYPE_ATTR = "type";
+ private static final String SHORTCUT_ID_ATTR = "shortcutId";
// Time attributes stored as an offset of the beginTime.
private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
@@ -106,9 +107,15 @@
event.mTimeStamp = statsOut.beginTime + XmlUtils.readLongAttribute(parser, TIME_ATTR);
event.mEventType = XmlUtils.readIntAttribute(parser, TYPE_ATTR);
- if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
- event.mConfiguration = new Configuration();
- Configuration.readXmlAttrs(parser, event.mConfiguration);
+ switch (event.mEventType) {
+ case UsageEvents.Event.CONFIGURATION_CHANGE:
+ event.mConfiguration = new Configuration();
+ Configuration.readXmlAttrs(parser, event.mConfiguration);
+ break;
+ case UsageEvents.Event.SHORTCUT_INVOCATION:
+ final String id = XmlUtils.readStringAttribute(parser, SHORTCUT_ID_ATTR);
+ event.mShortcutId = (id != null) ? id.intern() : null;
+ break;
}
if (statsOut.events == null) {
@@ -165,9 +172,17 @@
}
XmlUtils.writeIntAttribute(xml, TYPE_ATTR, event.mEventType);
- if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE
- && event.mConfiguration != null) {
- Configuration.writeXmlAttrs(xml, event.mConfiguration);
+ switch (event.mEventType) {
+ case UsageEvents.Event.CONFIGURATION_CHANGE:
+ if (event.mConfiguration != null) {
+ Configuration.writeXmlAttrs(xml, event.mConfiguration);
+ }
+ break;
+ case UsageEvents.Event.SHORTCUT_INVOCATION:
+ if (event.mShortcutId != null) {
+ XmlUtils.writeStringAttribute(xml, SHORTCUT_ID_ATTR, event.mShortcutId);
+ }
+ break;
}
xml.endTag(null, EVENT_TAG);
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 7d003f3..59e4c80 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -549,6 +549,9 @@
if (event.mConfiguration != null) {
pw.printPair("config", Configuration.resourceQualifierString(event.mConfiguration));
}
+ if (event.mShortcutId != null) {
+ pw.printPair("shortcutId", event.mShortcutId);
+ }
pw.println();
}
pw.decreaseIndent();
@@ -588,6 +591,8 @@
return "SYSTEM_INTERACTION";
case UsageEvents.Event.USER_INTERACTION:
return "USER_INTERACTION";
+ case UsageEvents.Event.SHORTCUT_INVOCATION:
+ return "SHORTCUT_INVOCATION";
default:
return "UNKNOWN";
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index df9242d..8560651 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -49,6 +49,7 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.SomeArgs;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.FgThread;
@@ -320,6 +321,7 @@
private boolean mConnected;
private boolean mHostConnected;
private boolean mSourcePower;
+ private boolean mSinkPower;
private boolean mConfigured;
private boolean mUsbDataUnlocked;
private String mCurrentFunctions;
@@ -401,7 +403,18 @@
public void updateHostState(UsbPort port, UsbPortStatus status) {
boolean hostConnected = status.getCurrentDataRole() == UsbPort.DATA_ROLE_HOST;
boolean sourcePower = status.getCurrentPowerRole() == UsbPort.POWER_ROLE_SOURCE;
- obtainMessage(MSG_UPDATE_HOST_STATE, hostConnected ? 1 :0, sourcePower ? 1 :0).sendToTarget();
+ boolean sinkPower = status.getCurrentPowerRole() == UsbPort.POWER_ROLE_SINK;
+
+ if (DEBUG) {
+ Slog.i(TAG, "updateHostState " + port + " status=" + status);
+ }
+
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = hostConnected ? 1 :0;
+ args.argi2 = sourcePower ? 1 :0;
+ args.argi3 = sinkPower ? 1 :0;
+
+ obtainMessage(MSG_UPDATE_HOST_STATE, args).sendToTarget();
}
private boolean waitForState(String state) {
@@ -718,8 +731,11 @@
}
break;
case MSG_UPDATE_HOST_STATE:
- mHostConnected = (msg.arg1 == 1);
- mSourcePower = (msg.arg2 == 1);
+ SomeArgs args = (SomeArgs) msg.obj;
+ mHostConnected = (args.argi1 == 1);
+ mSourcePower = (args.argi2 == 1);
+ mSinkPower = (args.argi3 == 1);
+ args.recycle();
updateUsbNotification();
if (mBootCompleted) {
updateUsbStateBroadcastIfNeeded();
@@ -809,6 +825,8 @@
}
} else if (mSourcePower) {
id = com.android.internal.R.string.usb_supplying_notification_title;
+ } else if (mHostConnected && mSinkPower) {
+ id = com.android.internal.R.string.usb_charging_notification_title;
}
if (id != mUsbNotificationId) {
// clear notification if title needs changing
@@ -908,6 +926,9 @@
pw.println(" mConfigured: " + mConfigured);
pw.println(" mUsbDataUnlocked: " + mUsbDataUnlocked);
pw.println(" mCurrentAccessory: " + mCurrentAccessory);
+ pw.println(" mHostConnected: " + mHostConnected);
+ pw.println(" mSourcePower: " + mSourcePower);
+ pw.println(" mSinkPower: " + mSinkPower);
try {
pw.println(" Kernel state: "
+ FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim());
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index 3779d87..e89585c 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -696,8 +696,13 @@
Slog.w(TAG, "Recognition aborted");
MetricsLogger.count(mContext, "sth_recognition_aborted", 1);
ModelData modelData = getModelDataForLocked(event.soundModelHandle);
- if (modelData != null) {
+ if (modelData != null && modelData.isModelStarted()) {
modelData.setStopped();
+ try {
+ modelData.getCallback().onRecognitionPaused();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException in onRecognitionPaused", e);
+ }
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
index 0f68cca..0dcd152 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -40,11 +40,12 @@
static final boolean DBG = false;
private static final String NAME = "sound_model.db";
- private static final int VERSION = 4;
+ private static final int VERSION = 5;
public static interface SoundModelContract {
public static final String TABLE = "sound_model";
public static final String KEY_MODEL_UUID = "model_uuid";
+ public static final String KEY_VENDOR_UUID = "vendor_uuid";
public static final String KEY_KEYPHRASE_ID = "keyphrase_id";
public static final String KEY_TYPE = "type";
public static final String KEY_DATA = "data";
@@ -58,6 +59,7 @@
private static final String CREATE_TABLE_SOUND_MODEL = "CREATE TABLE "
+ SoundModelContract.TABLE + "("
+ SoundModelContract.KEY_MODEL_UUID + " TEXT PRIMARY KEY,"
+ + SoundModelContract.KEY_VENDOR_UUID + " TEXT, "
+ SoundModelContract.KEY_KEYPHRASE_ID + " INTEGER,"
+ SoundModelContract.KEY_TYPE + " INTEGER,"
+ SoundModelContract.KEY_DATA + " BLOB,"
@@ -78,9 +80,19 @@
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- // TODO: For now, drop older tables and recreate new ones.
- db.execSQL("DROP TABLE IF EXISTS " + SoundModelContract.TABLE);
- onCreate(db);
+ if (oldVersion < 4) {
+ // For old versions just drop the tables and recreate new ones.
+ db.execSQL("DROP TABLE IF EXISTS " + SoundModelContract.TABLE);
+ onCreate(db);
+ } else {
+ // In the jump to version 5, we added support for the vendor UUID.
+ if (oldVersion == 4) {
+ Slog.d(TAG, "Adding vendor UUID column");
+ db.execSQL("ALTER TABLE " + SoundModelContract.TABLE + " ADD COLUMN "
+ + SoundModelContract.KEY_VENDOR_UUID + " TEXT");
+ oldVersion++;
+ }
+ }
}
/**
@@ -93,6 +105,9 @@
SQLiteDatabase db = getWritableDatabase();
ContentValues values = new ContentValues();
values.put(SoundModelContract.KEY_MODEL_UUID, soundModel.uuid.toString());
+ if (soundModel.vendorUuid != null) {
+ values.put(SoundModelContract.KEY_VENDOR_UUID, soundModel.vendorUuid.toString());
+ }
values.put(SoundModelContract.KEY_TYPE, SoundTrigger.SoundModel.TYPE_KEYPHRASE);
values.put(SoundModelContract.KEY_DATA, soundModel.data);
@@ -176,6 +191,11 @@
continue;
}
+ String vendorUuidString = null;
+ int vendorUuidColumn = c.getColumnIndex(SoundModelContract.KEY_VENDOR_UUID);
+ if (vendorUuidColumn != -1) {
+ vendorUuidString = c.getString(vendorUuidColumn);
+ }
byte[] data = c.getBlob(c.getColumnIndex(SoundModelContract.KEY_DATA));
int recognitionModes = c.getInt(
c.getColumnIndex(SoundModelContract.KEY_RECOGNITION_MODES));
@@ -212,9 +232,12 @@
Keyphrase[] keyphrases = new Keyphrase[1];
keyphrases[0] = new Keyphrase(
keyphraseId, recognitionModes, modelLocale, text, users);
+ UUID vendorUuid = null;
+ if (vendorUuidString != null) {
+ vendorUuid = UUID.fromString(vendorUuidString);
+ }
KeyphraseSoundModel model = new KeyphraseSoundModel(
- UUID.fromString(modelUuid),
- null /* FIXME use vendor UUID */, data, keyphrases);
+ UUID.fromString(modelUuid), vendorUuid, data, keyphrases);
if (DBG) {
Slog.d(TAG, "Found SoundModel for the given keyphrase/locale/user: "
+ model);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 23c58fe..43d2a1f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -41,6 +41,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
@@ -55,15 +56,16 @@
import android.util.Log;
import android.util.Slog;
+import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
-import com.android.server.soundtrigger.SoundTriggerInternal;
import com.android.server.SystemService;
import com.android.server.UiThread;
+import com.android.server.soundtrigger.SoundTriggerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -84,6 +86,9 @@
final TreeSet<Integer> mLoadedKeyphraseIds;
SoundTriggerInternal mSoundTriggerInternal;
+ private final RemoteCallbackList<IVoiceInteractionSessionListener>
+ mVoiceInteractionSessionListeners = new RemoteCallbackList<>();
+
public VoiceInteractionManagerService(Context context) {
super(context);
mContext = context;
@@ -1038,6 +1043,48 @@
}
@Override
+ public void registerVoiceInteractionSessionListener(
+ IVoiceInteractionSessionListener listener) {
+ enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
+ synchronized (this) {
+ mVoiceInteractionSessionListeners.register(listener);
+ }
+ }
+
+ public void onSessionShown() {
+ synchronized (this) {
+ final int size = mVoiceInteractionSessionListeners.beginBroadcast();
+ for (int i = 0; i < size; ++i) {
+ final IVoiceInteractionSessionListener listener =
+ mVoiceInteractionSessionListeners.getBroadcastItem(i);
+ try {
+ listener.onVoiceSessionShown();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error delivering voice interaction open event.", e);
+ }
+ }
+ mVoiceInteractionSessionListeners.finishBroadcast();
+ }
+ }
+
+ public void onSessionHidden() {
+ synchronized (this) {
+ final int size = mVoiceInteractionSessionListeners.beginBroadcast();
+ for (int i = 0; i < size; ++i) {
+ final IVoiceInteractionSessionListener listener =
+ mVoiceInteractionSessionListeners.getBroadcastItem(i);
+ try {
+ listener.onVoiceSessionHidden();
+
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error delivering voice interaction closed event.", e);
+ }
+ }
+ mVoiceInteractionSessionListeners.finishBroadcast();
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 3f9da4c..a46ccee 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -41,6 +41,7 @@
import android.util.Slog;
import android.view.IWindowManager;
+import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
import com.android.server.LocalServices;
@@ -58,7 +59,7 @@
final Context mContext;
final Handler mHandler;
- final Object mLock;
+ final VoiceInteractionManagerService.VoiceInteractionManagerServiceStub mServiceStub;
final int mUser;
final ComponentName mComponent;
final IActivityManager mAm;
@@ -77,7 +78,7 @@
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
String reason = intent.getStringExtra("reason");
if (!CLOSE_REASON_VOICE_INTERACTION.equals(reason) && !"dream".equals(reason)) {
- synchronized (mLock) {
+ synchronized (mServiceStub) {
if (mActiveSession != null && mActiveSession.mSession != null) {
try {
mActiveSession.mSession.closeSystemDialogs();
@@ -93,7 +94,7 @@
final ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- synchronized (mLock) {
+ synchronized (mServiceStub) {
mService = IVoiceInteractionService.Stub.asInterface(service);
try {
mService.ready();
@@ -108,11 +109,12 @@
}
};
- VoiceInteractionManagerServiceImpl(Context context, Handler handler, Object lock,
+ VoiceInteractionManagerServiceImpl(Context context, Handler handler,
+ VoiceInteractionManagerService.VoiceInteractionManagerServiceStub stub,
int userHandle, ComponentName service) {
mContext = context;
mHandler = handler;
- mLock = lock;
+ mServiceStub = stub;
mUser = userHandle;
mComponent = service;
mAm = ActivityManagerNative.getDefault();
@@ -148,8 +150,9 @@
public boolean showSessionLocked(Bundle args, int flags,
IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) {
if (mActiveSession == null) {
- mActiveSession = new VoiceInteractionSessionConnection(mLock, mSessionComponentName,
- mUser, mContext, this, mInfo.getServiceInfo().applicationInfo.uid, mHandler);
+ mActiveSession = new VoiceInteractionSessionConnection(mServiceStub,
+ mSessionComponentName, mUser, mContext, this,
+ mInfo.getServiceInfo().applicationInfo.uid, mHandler);
}
List<IBinder> activityTokens = null;
if (activityToken == null) {
@@ -355,8 +358,18 @@
@Override
public void sessionConnectionGone(VoiceInteractionSessionConnection connection) {
- synchronized (mLock) {
+ synchronized (mServiceStub) {
finishLocked(connection.mToken, false);
}
}
+
+ @Override
+ public void onSessionShown(VoiceInteractionSessionConnection connection) {
+ mServiceStub.onSessionShown();
+ }
+
+ @Override
+ public void onSessionHidden(VoiceInteractionSessionConnection connection) {
+ mServiceStub.onSessionHidden();
+ }
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 0694911..c3075b3 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -46,6 +46,7 @@
import android.view.IWindowManager;
import android.view.WindowManager;
+import com.android.internal.app.AssistUtils;
import com.android.internal.app.IAssistScreenshotReceiver;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
@@ -130,6 +131,8 @@
public interface Callback {
public void sessionConnectionGone(VoiceInteractionSessionConnection connection);
+ public void onSessionShown(VoiceInteractionSessionConnection connection);
+ public void onSessionHidden(VoiceInteractionSessionConnection connection);
}
final ServiceConnection mFullConnection = new ServiceConnection() {
@@ -299,7 +302,7 @@
} else {
mScreenshot = null;
}
- if (needDisclosure) {
+ if (needDisclosure && AssistUtils.shouldDisclose(mContext, mSessionComponentName)) {
mHandler.post(mShowAssistDisclosureRunnable);
}
if (mSession != null) {
@@ -313,6 +316,7 @@
} else if (showCallback != null) {
mPendingShowCallbacks.add(showCallback);
}
+ mCallback.onSessionShown(this);
return true;
}
if (showCallback != null) {
@@ -468,6 +472,7 @@
} catch (RemoteException e) {
}
}
+ mCallback.onSessionHidden(this);
}
if (mFullyBound) {
mContext.unbindService(mFullConnection);
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 7d7e1eb..62625bdf 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -23,6 +23,7 @@
import java.lang.String;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -104,7 +105,6 @@
* An {@link InCallService} will only see this state if it has the
* {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true} in its
* manifest.
- * @hide
*/
public static final int STATE_PULLING_CALL = 11;
@@ -252,7 +252,6 @@
* <p>
* See {@link Connection#CAPABILITY_CAN_PULL_CALL} and
* {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
- * @hide
*/
public static final int CAPABILITY_CAN_PULL_CALL = 0x00800000;
@@ -305,10 +304,14 @@
* in its manifest.
* <p>
* See {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
- * @hide
*/
public static final int PROPERTY_IS_EXTERNAL_CALL = 0x00000040;
+ /**
+ * Indicates that the call has CDMA Enhanced Voice Privacy enabled.
+ */
+ public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 0x00000080;
+
//******************************************************************************************
// Next PROPERTY value: 0x00000100
//******************************************************************************************
@@ -465,6 +468,9 @@
if (hasProperty(properties, PROPERTY_IS_EXTERNAL_CALL)) {
builder.append(" PROPERTY_IS_EXTERNAL_CALL");
}
+ if(hasProperty(properties, PROPERTY_HAS_CDMA_VOICE_PRIVACY)) {
+ builder.append(" PROPERTY_HAS_CDMA_VOICE_PRIVACY");
+ }
builder.append("]");
return builder.toString();
}
@@ -695,6 +701,24 @@
}
}
+ /**
+ * Defines callbacks which inform the {@link InCallService} of changes to a {@link Call}.
+ * These callbacks can originate from the Telecom framework, or a {@link ConnectionService}
+ * implementation.
+ * <p>
+ * You can handle these callbacks by extending the {@link Callback} class and overriding the
+ * callbacks that your {@link InCallService} is interested in. The callback methods include the
+ * {@link Call} for which the callback applies, allowing reuse of a single instance of your
+ * {@link Callback} implementation, if desired.
+ * <p>
+ * Use {@link Call#registerCallback(Callback)} to register your callback(s). Ensure
+ * {@link Call#unregisterCallback(Callback)} is called when you no longer require callbacks
+ * (typically in {@link InCallService#onCallRemoved(Call)}).
+ * Note: Callbacks which occur before you call {@link Call#registerCallback(Callback)} will not
+ * reach your implementation of {@link Callback}, so it is important to register your callback
+ * as soon as your {@link InCallService} is notified of a new call via
+ * {@link InCallService#onCallAdded(Call)}.
+ */
public static abstract class Callback {
/**
* Invoked when the state of this {@code Call} has changed. See {@link #getState()}.
@@ -779,14 +803,19 @@
public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {}
/**
- * Invoked when a call receives an event from its associated {@link Connection}.
+ * Invoked when a {@link Call} receives an event from its associated {@link Connection}.
+ * <p>
+ * Where possible, the Call should make an attempt to handle {@link Connection} events which
+ * are part of the {@code android.telecom.*} namespace. The Call should ignore any events
+ * it does not wish to handle. Unexpected events should be handled gracefully, as it is
+ * possible that a {@link ConnectionService} has defined its own Connection events which a
+ * Call is not aware of.
* <p>
* See {@link Connection#sendConnectionEvent(String, Bundle)}.
*
* @param call The {@code Call} receiving the event.
* @param event The event.
* @param extras Extras associated with the connection event.
- * @hide
*/
public void onConnectionEvent(Call call, String event, Bundle extras) {}
}
@@ -965,7 +994,6 @@
* An {@link InCallService} will only see calls which support this method if it has the
* {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true}
* in its manifest.
- * @hide
*/
public void pullExternalCall() {
// If this isn't an external call, ignore the request.
@@ -980,15 +1008,35 @@
* Sends a {@code Call} event from this {@code Call} to the associated {@link Connection} in
* the {@link ConnectionService}.
* <p>
+ * Call events are used to communicate point in time information from an {@link InCallService}
+ * to a {@link ConnectionService}. A {@link ConnectionService} implementation could define
+ * events which enable the {@link InCallService}, for example, toggle a unique feature of the
+ * {@link ConnectionService}.
+ * <p>
+ * A {@link ConnectionService} can communicate to the {@link InCallService} using
+ * {@link Connection#sendConnectionEvent(String, Bundle)}.
+ * <p>
* Events are exposed to {@link ConnectionService} implementations via
* {@link android.telecom.Connection#onCallEvent(String, Bundle)}.
* <p>
* No assumptions should be made as to how a {@link ConnectionService} will handle these events.
- * Events should be fully qualified (e.g., com.example.event.MY_EVENT) to avoid conflicts.
+ * The {@link InCallService} must assume that the {@link ConnectionService} could chose to
+ * ignore some events altogether.
+ * <p>
+ * Events should be fully qualified (e.g., {@code com.example.event.MY_EVENT}) to avoid
+ * conflicts between {@link InCallService} implementations. Further, {@link InCallService}
+ * implementations shall not re-purpose events in the {@code android.*} namespace, nor shall
+ * they define their own event types in this namespace. When defining a custom event type,
+ * ensure the contents of the extras {@link Bundle} is clearly defined. Extra keys for this
+ * bundle should be named similar to the event type (e.g. {@code com.example.extra.MY_EXTRA}).
+ * <p>
+ * When defining events and the associated extras, it is important to keep their behavior
+ * consistent when the associated {@link InCallService} is updated. Support for deprecated
+ * events/extras should me maintained to ensure backwards compatibility with older
+ * {@link ConnectionService} implementations which were built to support the older behavior.
*
* @param event The connection event.
* @param extras Bundle containing extra information associated with the event.
- * @hide
*/
public void sendCallEvent(String event, Bundle extras) {
mInCallAdapter.sendCallEvent(mTelecomCallId, event, extras);
@@ -1002,7 +1050,6 @@
* extras. Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
*
* @param extras The extras to add.
- * @hide
*/
public final void putExtras(Bundle extras) {
if (extras == null) {
@@ -1032,7 +1079,7 @@
}
/**
- * Adds an integer extra to this {@code Connection}.
+ * Adds an integer extra to this {@link Call}.
*
* @param key The extra key.
* @param value The value.
@@ -1047,7 +1094,7 @@
}
/**
- * Adds a string extra to this {@code Connection}.
+ * Adds a string extra to this {@link Call}.
*
* @param key The extra key.
* @param value The value.
@@ -1062,10 +1109,9 @@
}
/**
- * Removes extras from this {@code Connection}.
+ * Removes extras from this {@link Call}.
*
* @param keys The keys of the extras to remove.
- * @hide
*/
public final void removeExtras(List<String> keys) {
if (mExtras != null) {
@@ -1080,6 +1126,15 @@
}
/**
+ * Removes extras from this {@link Call}.
+ *
+ * @param keys The keys of the extras to remove.
+ */
+ public final void removeExtras(String ... keys) {
+ removeExtras(Arrays.asList(keys));
+ }
+
+ /**
* Obtains the parent of this {@code Call} in a conference, if any.
*
* @return The parent {@code Call}, or {@code null} if this {@code Call} is not a
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 0227d27..a012082 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -24,6 +24,7 @@
import android.util.ArraySet;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -164,7 +165,6 @@
* {@link Connection} for valid values.
*
* @return A bitmask of the properties of the conference call.
- * @hide
*/
public final int getConnectionProperties() {
return mConnectionProperties;
@@ -256,60 +256,63 @@
}
/**
- * Invoked when the Conference and all it's {@link Connection}s should be disconnected.
+ * Notifies the {@link Conference} when the Conference and all it's {@link Connection}s should
+ * be disconnected.
*/
public void onDisconnect() {}
/**
- * Invoked when the specified {@link Connection} should be separated from the conference call.
+ * Notifies the {@link Conference} when the specified {@link Connection} should be separated
+ * from the conference call.
*
* @param connection The connection to separate.
*/
public void onSeparate(Connection connection) {}
/**
- * Invoked when the specified {@link Connection} should merged with the conference call.
+ * Notifies the {@link Conference} when the specified {@link Connection} should merged with the
+ * conference call.
*
* @param connection The {@code Connection} to merge.
*/
public void onMerge(Connection connection) {}
/**
- * Invoked when the conference should be put on hold.
+ * Notifies the {@link Conference} when it should be put on hold.
*/
public void onHold() {}
/**
- * Invoked when the conference should be moved from hold to active.
+ * Notifies the {@link Conference} when it should be moved from a held to active state.
*/
public void onUnhold() {}
/**
- * Invoked when the child calls should be merged. Only invoked if the conference contains the
- * capability {@link Connection#CAPABILITY_MERGE_CONFERENCE}.
+ * Notifies the {@link Conference} when the child calls should be merged. Only invoked if the
+ * conference contains the capability {@link Connection#CAPABILITY_MERGE_CONFERENCE}.
*/
public void onMerge() {}
/**
- * Invoked when the child calls should be swapped. Only invoked if the conference contains the
- * capability {@link Connection#CAPABILITY_SWAP_CONFERENCE}.
+ * Notifies the {@link Conference} when the child calls should be swapped. Only invoked if the
+ * conference contains the capability {@link Connection#CAPABILITY_SWAP_CONFERENCE}.
*/
public void onSwap() {}
/**
- * Notifies this conference of a request to play a DTMF tone.
+ * Notifies the {@link Conference} of a request to play a DTMF tone.
*
* @param c A DTMF character.
*/
public void onPlayDtmfTone(char c) {}
/**
- * Notifies this conference of a request to stop any currently playing DTMF tones.
+ * Notifies the {@link Conference} of a request to stop any currently playing DTMF tones.
*/
public void onStopDtmfTone() {}
/**
- * Notifies this conference that the {@link #getAudioState()} property has a new value.
+ * Notifies the {@link Conference} that the {@link #getAudioState()} property has a new value.
*
* @param state The new call audio state.
* @deprecated Use {@link #onCallAudioStateChanged(CallAudioState)} instead.
@@ -320,14 +323,15 @@
public void onAudioStateChanged(AudioState state) {}
/**
- * Notifies this conference that the {@link #getCallAudioState()} property has a new value.
+ * Notifies the {@link Conference} that the {@link #getCallAudioState()} property has a new
+ * value.
*
* @param state The new call audio state.
*/
public void onCallAudioStateChanged(CallAudioState state) {}
/**
- * Notifies this conference that a connection has been added to it.
+ * Notifies the {@link Conference} that a {@link Connection} has been added to it.
*
* @param connection The newly added connection.
*/
@@ -396,7 +400,6 @@
* {@link Connection} for valid values.
*
* @param connectionProperties A bitmask of the {@code Properties} of the conference call.
- * @hide
*/
public final void setConnectionProperties(int connectionProperties) {
if (connectionProperties != mConnectionProperties) {
@@ -681,8 +684,11 @@
* New or existing keys are replaced in the {@code Conference} extras. Keys which are no longer
* in the new extras, but were present the last time {@code setExtras} was called are removed.
* <p>
+ * Alternatively you may use the {@link #putExtras(Bundle)}, and
+ * {@link #removeExtras(String...)} methods to modify the extras.
+ * <p>
* No assumptions should be made as to how an In-Call UI or service will handle these extras.
- * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
+ * Keys should be fully qualified (e.g., com.example.extras.MY_EXTRA) to avoid conflicts.
*
* @param extras The extras associated with this {@code Conference}.
*/
@@ -727,7 +733,6 @@
* Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
*
* @param extras The extras to add.
- * @hide
*/
public final void putExtras(@NonNull Bundle extras) {
if (extras == null) {
@@ -790,10 +795,9 @@
}
/**
- * Removes an extra from this {@link Conference}.
+ * Removes extras from this {@link Conference}.
*
- * @param keys The key of the extra key to remove.
- * @hide
+ * @param keys The keys of the extras to remove.
*/
public final void removeExtras(List<String> keys) {
if (keys == null || keys.isEmpty()) {
@@ -815,7 +819,25 @@
}
/**
+ * Removes extras from this {@link Conference}.
+ *
+ * @param keys The keys of the extras to remove.
+ */
+ public final void removeExtras(String ... keys) {
+ removeExtras(Arrays.asList(keys));
+ }
+
+ /**
* Returns the extras associated with this conference.
+ * <p>
+ * Extras should be updated using {@link #putExtras(Bundle)} and {@link #removeExtras(List)}.
+ * <p>
+ * Telecom or an {@link InCallService} can also update the extras via
+ * {@link android.telecom.Call#putExtras(Bundle)}, and
+ * {@link Call#removeExtras(List)}.
+ * <p>
+ * The conference is notified of changes to the extras made by Telecom or an
+ * {@link InCallService} by {@link #onExtrasChanged(Bundle)}.
*
* @return The extras associated with this connection.
*/
@@ -832,7 +854,6 @@
* {@link Call#removeExtras(List)}.
*
* @param extras The new extras bundle.
- * @hide
*/
public void onExtrasChanged(Bundle extras) {}
diff --git a/telecomm/java/android/telecom/ConferenceParticipant.java b/telecomm/java/android/telecom/ConferenceParticipant.java
index db0f151..20b04eb 100644
--- a/telecomm/java/android/telecom/ConferenceParticipant.java
+++ b/telecomm/java/android/telecom/ConferenceParticipant.java
@@ -114,13 +114,13 @@
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[ConferenceParticipant Handle: ");
- sb.append(mHandle);
+ sb.append(Log.pii(mHandle));
sb.append(" DisplayName: ");
- sb.append(mDisplayName);
+ sb.append(Log.pii(mDisplayName));
sb.append(" Endpoint: ");
- sb.append(mEndpoint);
+ sb.append(Log.pii(mEndpoint));
sb.append(" State: ");
- sb.append(mState);
+ sb.append(Connection.stateToString(mState));
sb.append("]");
return sb.toString();
}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index ff220f3..a093d54 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -35,6 +35,7 @@
import android.view.Surface;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -52,6 +53,37 @@
* Implementations are then responsible for updating the state of the {@code Connection}, and
* must call {@link #destroy()} to signal to the framework that the {@code Connection} is no
* longer used and associated resources may be recovered.
+ * <p>
+ * Subclasses of {@code Connection} override the {@code on*} methods to provide the the
+ * {@link ConnectionService}'s implementation of calling functionality. The {@code on*} methods are
+ * called by Telecom to inform an instance of a {@code Connection} of actions specific to that
+ * {@code Connection} instance.
+ * <p>
+ * Basic call support requires overriding the following methods: {@link #onAnswer()},
+ * {@link #onDisconnect()}, {@link #onReject()}, {@link #onAbort()}
+ * <p>
+ * Where a {@code Connection} has {@link #CAPABILITY_SUPPORT_HOLD}, the {@link #onHold()} and
+ * {@link #onUnhold()} methods should be overridden to provide hold support for the
+ * {@code Connection}.
+ * <p>
+ * Where a {@code Connection} supports a variation of video calling (e.g. the
+ * {@code CAPABILITY_SUPPORTS_VT_*} capability bits), {@link #onAnswer(int)} should be overridden
+ * to support answering a call as a video call.
+ * <p>
+ * Where a {@code Connection} has {@link #PROPERTY_IS_EXTERNAL_CALL} and
+ * {@link #CAPABILITY_CAN_PULL_CALL}, {@link #onPullExternalCall()} should be overridden to provide
+ * support for pulling the external call.
+ * <p>
+ * Where a {@code Connection} supports conference calling {@link #onSeparate()} should be
+ * overridden.
+ * <p>
+ * There are a number of other {@code on*} methods which a {@code Connection} can choose to
+ * implement, depending on whether it is concerned with the associated calls from Telecom. If,
+ * for example, call events from a {@link InCallService} are handled,
+ * {@link #onCallEvent(String, Bundle)} should be overridden. Another example is
+ * {@link #onExtrasChanged(Bundle)}, which should be overridden if the {@code Connection} wishes to
+ * make use of extra information provided via the {@link Call#putExtras(Bundle)} and
+ * {@link Call#removeExtras(String...)} methods.
*/
public abstract class Connection extends Conferenceable {
@@ -100,7 +132,6 @@
* <p>
* A connection can only be in this state if the {@link #PROPERTY_IS_EXTERNAL_CALL} property and
* {@link #CAPABILITY_CAN_PULL_CALL} capability bits are set on the connection.
- * @hide
*/
public static final int STATE_PULLING_CALL = 7;
@@ -284,7 +315,6 @@
* <p>
* Should only be set on a {@code Connection} where {@link #PROPERTY_IS_EXTERNAL_CALL}
* is set.
- * @hide
*/
public static final int CAPABILITY_CAN_PULL_CALL = 0x01000000;
@@ -332,13 +362,22 @@
* external connections. Only those {@link InCallService}s which have the
* {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true} in its
* manifest will see external connections.
- * @hide
*/
public static final int PROPERTY_IS_EXTERNAL_CALL = 1<<4;
+ /**
+ * Indicates that the connection has CDMA Enhanced Voice Privacy enabled.
+ */
+ public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 1<<5;
+
+ /**
+ * Indicates that the connection represents a downgraded IMS conference.
+ * @hide
+ */
+ public static final int PROPERTY_IS_DOWNGRADED_CONFERENCE = 1<<6;
//**********************************************************************************************
- // Next PROPERTY value: 1<<5
+ // Next PROPERTY value: 1<<7
//**********************************************************************************************
/**
@@ -365,9 +404,25 @@
public static final String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
/**
+ * Boolean connection extra key set on a {@link Connection} in
+ * {@link Connection#STATE_RINGING} state to indicate that answering the call will cause the
+ * current active foreground call to be dropped.
+ */
+ public static final String EXTRA_ANSWERING_DROPS_FG_CALL =
+ "android.telecom.extra.ANSWERING_DROPS_FG_CALL";
+
+ /**
+ * Boolean connection extra key on a {@link Connection} which indicates that adding an
+ * additional call is disallowed.
+ * @hide
+ */
+ public static final String EXTRA_DISABLE_ADD_CALL =
+ "android.telecom.extra.DISABLE_ADD_CALL";
+
+ /**
* Connection event used to inform Telecom that it should play the on hold tone. This is used
* to play a tone when the peer puts the current call on hold. Sent to Telecom via
- * {@link #sendConnectionEvent(String)}.
+ * {@link #sendConnectionEvent(String, Bundle)}.
* @hide
*/
public static final String EVENT_ON_HOLD_TONE_START =
@@ -376,7 +431,7 @@
/**
* Connection event used to inform Telecom that it should stop the on hold tone. This is used
* to stop a tone when the peer puts the current call on hold. Sent to Telecom via
- * {@link #sendConnectionEvent(String)}.
+ * {@link #sendConnectionEvent(String, Bundle)}.
* @hide
*/
public static final String EVENT_ON_HOLD_TONE_END =
@@ -391,10 +446,47 @@
* {@link Call.Details#PROPERTY_IS_EXTERNAL_CALL} and
* {@link Call.Details#CAPABILITY_CAN_PULL_CALL}, but the {@link ConnectionService} could not
* pull the external call due to an error condition.
- * @hide
+ * <p>
+ * Sent via {@link #sendConnectionEvent(String, Bundle)}. The {@link Bundle} parameter is
+ * expected to be null when this connection event is used.
*/
public static final String EVENT_CALL_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED";
+ /**
+ * Connection event used to inform {@link InCallService}s when the merging of two calls has
+ * failed. The User Interface should use this message to inform the user of the error.
+ * <p>
+ * Sent via {@link #sendConnectionEvent(String, Bundle)}. The {@link Bundle} parameter is
+ * expected to be null when this connection event is used.
+ */
+ public static final String EVENT_CALL_MERGE_FAILED = "android.telecom.event.CALL_MERGE_FAILED";
+
+ /**
+ * Connection event used to inform {@link InCallService}s when a call has been put on hold by
+ * the remote party.
+ * <p>
+ * This is different than the {@link Connection#STATE_HOLDING} state which indicates that the
+ * call is being held locally on the device. When a capable {@link ConnectionService} receives
+ * signalling to indicate that the remote party has put the call on hold, it can send this
+ * connection event.
+ * @hide
+ */
+ public static final String EVENT_CALL_REMOTELY_HELD =
+ "android.telecom.event.CALL_REMOTELY_HELD";
+
+ /**
+ * Connection event used to inform {@link InCallService}s when a call which was remotely held
+ * (see {@link #EVENT_CALL_REMOTELY_HELD}) has been un-held by the remote party.
+ * <p>
+ * This is different than the {@link Connection#STATE_HOLDING} state which indicates that the
+ * call is being held locally on the device. When a capable {@link ConnectionService} receives
+ * signalling to indicate that the remote party has taken the call off hold, it can send this
+ * connection event.
+ * @hide
+ */
+ public static final String EVENT_CALL_REMOTELY_UNHELD =
+ "android.telecom.event.CALL_REMOTELY_UNHELD";
+
// Flag controlling whether PII is emitted into the logs
private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
@@ -441,69 +533,94 @@
mConnectionCapabilities |= capability;
}
-
+ /**
+ * Renders a set of capability bits ({@code CAPABILITY_*}) as a human readable string.
+ *
+ * @param capabilities A capability bit field.
+ * @return A human readable string representation.
+ */
public static String capabilitiesToString(int capabilities) {
+ return capabilitiesToStringInternal(capabilities, true /* isLong */);
+ }
+
+ /**
+ * Renders a set of capability bits ({@code CAPABILITY_*}) as a *short* human readable
+ * string.
+ *
+ * @param capabilities A capability bit field.
+ * @return A human readable string representation.
+ * @hide
+ */
+ public static String capabilitiesToStringShort(int capabilities) {
+ return capabilitiesToStringInternal(capabilities, false /* isLong */);
+ }
+
+ private static String capabilitiesToStringInternal(int capabilities, boolean isLong) {
StringBuilder builder = new StringBuilder();
- builder.append("[Capabilities:");
+ builder.append("[");
+ if (isLong) {
+ builder.append("Capabilities:");
+ }
+
if (can(capabilities, CAPABILITY_HOLD)) {
- builder.append(" CAPABILITY_HOLD");
+ builder.append(isLong ? " CAPABILITY_HOLD" : " hld");
}
if (can(capabilities, CAPABILITY_SUPPORT_HOLD)) {
- builder.append(" CAPABILITY_SUPPORT_HOLD");
+ builder.append(isLong ? " CAPABILITY_SUPPORT_HOLD" : " sup_hld");
}
if (can(capabilities, CAPABILITY_MERGE_CONFERENCE)) {
- builder.append(" CAPABILITY_MERGE_CONFERENCE");
+ builder.append(isLong ? " CAPABILITY_MERGE_CONFERENCE" : " mrg_cnf");
}
if (can(capabilities, CAPABILITY_SWAP_CONFERENCE)) {
- builder.append(" CAPABILITY_SWAP_CONFERENCE");
+ builder.append(isLong ? " CAPABILITY_SWAP_CONFERENCE" : " swp_cnf");
}
if (can(capabilities, CAPABILITY_RESPOND_VIA_TEXT)) {
- builder.append(" CAPABILITY_RESPOND_VIA_TEXT");
+ builder.append(isLong ? " CAPABILITY_RESPOND_VIA_TEXT" : " txt");
}
if (can(capabilities, CAPABILITY_MUTE)) {
- builder.append(" CAPABILITY_MUTE");
+ builder.append(isLong ? " CAPABILITY_MUTE" : " mut");
}
if (can(capabilities, CAPABILITY_MANAGE_CONFERENCE)) {
- builder.append(" CAPABILITY_MANAGE_CONFERENCE");
+ builder.append(isLong ? " CAPABILITY_MANAGE_CONFERENCE" : " mng_cnf");
}
if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_RX)) {
- builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_RX");
+ builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_LOCAL_RX" : " VTlrx");
}
if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_TX)) {
- builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_TX");
+ builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_LOCAL_TX" : " VTltx");
}
if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) {
- builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL");
+ builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL" : " VTlbi");
}
if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_RX)) {
- builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_RX");
+ builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_REMOTE_RX" : " VTrrx");
}
if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) {
- builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_TX");
+ builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_REMOTE_TX" : " VTrtx");
}
if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
- builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL");
+ builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL" : " VTrbi");
}
if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
- builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO");
+ builder.append(isLong ? " CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO" : " !v2a");
}
if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
- builder.append(" CAPABILITY_SPEED_UP_MT_AUDIO");
+ builder.append(isLong ? " CAPABILITY_SPEED_UP_MT_AUDIO" : " spd_aud");
}
if (can(capabilities, CAPABILITY_CAN_UPGRADE_TO_VIDEO)) {
- builder.append(" CAPABILITY_CAN_UPGRADE_TO_VIDEO");
+ builder.append(isLong ? " CAPABILITY_CAN_UPGRADE_TO_VIDEO" : " a2v");
}
if (can(capabilities, CAPABILITY_CAN_PAUSE_VIDEO)) {
- builder.append(" CAPABILITY_CAN_PAUSE_VIDEO");
+ builder.append(isLong ? " CAPABILITY_CAN_PAUSE_VIDEO" : " paus_VT");
}
if (can(capabilities, CAPABILITY_CONFERENCE_HAS_NO_CHILDREN)) {
- builder.append(" CAPABILITY_SINGLE_PARTY_CONFERENCE");
+ builder.append(isLong ? " CAPABILITY_SINGLE_PARTY_CONFERENCE" : " 1p_cnf");
}
if (can(capabilities, CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) {
- builder.append(" CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION");
+ builder.append(isLong ? " CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION" : " rsp_by_con");
}
if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
- builder.append(" CAPABILITY_CAN_PULL_CALL");
+ builder.append(isLong ? " CAPABILITY_CAN_PULL_CALL" : " pull");
}
builder.append("]");
@@ -511,34 +628,55 @@
}
/**
- * Builds a string representation of a properties bit-mask.
+ * Renders a set of property bits ({@code PROPERTY_*}) as a human readable string.
*
- * @param properties The properties bit-mask.
- * @return String representation.
- * @hide
+ * @param properties A property bit field.
+ * @return A human readable string representation.
*/
public static String propertiesToString(int properties) {
+ return propertiesToStringInternal(properties, true /* isLong */);
+ }
+
+ /**
+ * Renders a set of property bits ({@code PROPERTY_*}) as a *short* human readable string.
+ *
+ * @param properties A property bit field.
+ * @return A human readable string representation.
+ * @hide
+ */
+ public static String propertiesToStringShort(int properties) {
+ return propertiesToStringInternal(properties, false /* isLong */);
+ }
+
+ private static String propertiesToStringInternal(int properties, boolean isLong) {
StringBuilder builder = new StringBuilder();
- builder.append("[Properties:");
+ builder.append("[");
+ if (isLong) {
+ builder.append("Properties:");
+ }
if (can(properties, PROPERTY_SHOW_CALLBACK_NUMBER)) {
- builder.append(" PROPERTY_SHOW_CALLBACK_NUMBER");
+ builder.append(isLong ? " PROPERTY_SHOW_CALLBACK_NUMBER" : " clbk");
}
if (can(properties, PROPERTY_HIGH_DEF_AUDIO)) {
- builder.append(" PROPERTY_HIGH_DEF_AUDIO");
+ builder.append(isLong ? " PROPERTY_HIGH_DEF_AUDIO" : " HD");
}
if (can(properties, PROPERTY_WIFI)) {
- builder.append(" PROPERTY_WIFI");
+ builder.append(isLong ? " PROPERTY_WIFI" : " wifi");
}
if (can(properties, PROPERTY_GENERIC_CONFERENCE)) {
- builder.append(" PROPERTY_GENERIC_CONFERENCE");
+ builder.append(isLong ? " PROPERTY_GENERIC_CONFERENCE" : " gen_conf");
}
if (can(properties, PROPERTY_IS_EXTERNAL_CALL)) {
- builder.append(" PROPERTY_IS_EXTERNAL_CALL");
+ builder.append(isLong ? " PROPERTY_IS_EXTERNAL_CALL" : " xtrnl");
+ }
+
+ if (can(properties, PROPERTY_HAS_CDMA_VOICE_PRIVACY)) {
+ builder.append(isLong ? " PROPERTY_HAS_CDMA_VOICE_PRIVACY" : " priv");
}
builder.append("]");
@@ -574,6 +712,8 @@
public void onExtrasChanged(Connection c, Bundle extras) {}
public void onExtrasRemoved(Connection c, List<String> keys) {}
public void onConnectionEvent(Connection c, String event, Bundle extras) {}
+ /** @hide */
+ public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {}
}
/**
@@ -1385,6 +1525,15 @@
/**
* Returns the extras associated with this connection.
+ * <p>
+ * Extras should be updated using {@link #putExtras(Bundle)}.
+ * <p>
+ * Telecom or an {@link InCallService} can also update the extras via
+ * {@link android.telecom.Call#putExtras(Bundle)}, and
+ * {@link Call#removeExtras(List)}.
+ * <p>
+ * The connection is notified of changes to the extras made by Telecom or an
+ * {@link InCallService} by {@link #onExtrasChanged(Bundle)}.
*
* @return The extras associated with this connection.
*/
@@ -1472,6 +1621,8 @@
return "RINGING";
case STATE_DIALING:
return "DIALING";
+ case STATE_PULLING_CALL:
+ return "PULLING_CALL";
case STATE_ACTIVE:
return "ACTIVE";
case STATE_HOLDING:
@@ -1493,7 +1644,6 @@
/**
* Returns the connection's properties, as a bit mask of the {@code PROPERTY_*} constants.
- * @hide
*/
public final int getConnectionProperties() {
return mConnectionProperties;
@@ -1594,6 +1744,16 @@
}
/**
+ * Sets state to pulling (e.g. the connection is being pulled to the local device from another
+ * device). Only applicable for {@link Connection}s with
+ * {@link Connection#PROPERTY_IS_EXTERNAL_CALL} and {@link Connection#CAPABILITY_CAN_PULL_CALL}.
+ */
+ public final void setPulling() {
+ checkImmutable();
+ setState(STATE_PULLING_CALL);
+ }
+
+ /**
* Sets state to be on hold.
*/
public final void setOnHold() {
@@ -1699,7 +1859,6 @@
* Sets the connection's properties as a bit mask of the {@code PROPERTY_*} constants.
*
* @param connectionProperties The new connection properties.
- * @hide
*/
public final void setConnectionProperties(int connectionProperties) {
checkImmutable();
@@ -1880,6 +2039,9 @@
* New or existing keys are replaced in the {@code Connection} extras. Keys which are no longer
* in the new extras, but were present the last time {@code setExtras} was called are removed.
* <p>
+ * Alternatively you may use the {@link #putExtras(Bundle)}, and
+ * {@link #removeExtras(String...)} methods to modify the extras.
+ * <p>
* No assumptions should be made as to how an In-Call UI or service will handle these extras.
* Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
*
@@ -1924,7 +2086,6 @@
* Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
*
* @param extras The extras to add.
- * @hide
*/
public final void putExtras(@NonNull Bundle extras) {
checkImmutable();
@@ -1988,10 +2149,9 @@
}
/**
- * Removes an extra from this {@code Connection}.
+ * Removes extras from this {@code Connection}.
*
- * @param keys The key of the extra key to remove.
- * @hide
+ * @param keys The keys of the extras to remove.
*/
public final void removeExtras(List<String> keys) {
synchronized (mExtrasLock) {
@@ -2008,6 +2168,15 @@
}
/**
+ * Removes extras from this {@code Connection}.
+ *
+ * @param keys The keys of the extras to remove.
+ */
+ public final void removeExtras(String ... keys) {
+ removeExtras(Arrays.asList(keys));
+ }
+
+ /**
* Notifies this Connection that the {@link #getAudioState()} property has a new value.
*
* @param state The new connection audio state.
@@ -2129,7 +2298,6 @@
* capability and {@link Connection#PROPERTY_IS_EXTERNAL_CALL} property bits must be set.
* <p>
* For more information on external calls, see {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
- * @hide
*/
public void onPullExternalCall() {}
@@ -2138,11 +2306,16 @@
* <p>
* The {@link InCallService} issues a Call event via {@link Call#sendCallEvent(String, Bundle)}.
* <p>
+ * Where possible, the Connection should make an attempt to handle {@link Call} events which
+ * are part of the {@code android.telecom.*} namespace. The Connection should ignore any events
+ * it does not wish to handle. Unexpected events should be handled gracefully, as it is
+ * possible that a {@link InCallService} has defined its own Call events which a Connection is
+ * not aware of.
+ * <p>
* See also {@link Call#sendCallEvent(String, Bundle)}.
*
* @param event The call event.
* @param extras Extras associated with the call event.
- * @hide
*/
public void onCallEvent(String event, Bundle extras) {}
@@ -2155,7 +2328,6 @@
* {@link Call#removeExtras(List)}.
*
* @param extras The new extras bundle.
- * @hide
*/
public void onExtrasChanged(Bundle extras) {}
@@ -2330,17 +2502,54 @@
}
/**
- * Sends an event associated with this {@code Connection}, with associated event extras.
- *
- * Events are exposed to {@link InCallService} implementations via the
- * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)} API.
- *
+ * Notifies listeners when a change has occurred to the Connection which impacts its ability to
+ * be a part of a conference call.
+ * @param isConferenceSupported {@code true} if the connection supports being part of a
+ * conference call, {@code false} otherwise.
+ * @hide
+ */
+ protected void notifyConferenceSupportedChanged(boolean isConferenceSupported) {
+ for (Listener l : mListeners) {
+ l.onConferenceSupportedChanged(this, isConferenceSupported);
+ }
+ }
+
+ /**
+ * Sends an event associated with this {@code Connection} with associated event extras to the
+ * {@link InCallService}.
+ * <p>
+ * Connection events are used to communicate point in time information from a
+ * {@link ConnectionService} to a {@link InCallService} implementations. An example of a
+ * custom connection event includes notifying the UI when a WIFI call has been handed over to
+ * LTE, which the InCall UI might use to inform the user that billing charges may apply. The
+ * Android Telephony framework will send the {@link #EVENT_CALL_MERGE_FAILED} connection event
+ * when a call to {@link Call#mergeConference()} has failed to complete successfully. A
+ * connection event could also be used to trigger UI in the {@link InCallService} which prompts
+ * the user to make a choice (e.g. whether they want to incur roaming costs for making a call),
+ * which is communicated back via {@link Call#sendCallEvent(String, Bundle)}.
+ * <p>
+ * Events are exposed to {@link InCallService} implementations via
+ * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}.
+ * <p>
* No assumptions should be made as to how an In-Call UI or service will handle these events.
- * Events should be fully qualified (e.g., com.example.event.MY_EVENT) to avoid conflicts.
+ * The {@link ConnectionService} must assume that the In-Call UI could even chose to ignore
+ * some events altogether.
+ * <p>
+ * Events should be fully qualified (e.g. {@code com.example.event.MY_EVENT}) to avoid
+ * conflicts between {@link ConnectionService} implementations. Further, custom
+ * {@link ConnectionService} implementations shall not re-purpose events in the
+ * {@code android.*} namespace, nor shall they define new event types in this namespace. When
+ * defining a custom event type, ensure the contents of the extras {@link Bundle} is clearly
+ * defined. Extra keys for this bundle should be named similar to the event type (e.g.
+ * {@code com.example.extra.MY_EXTRA}).
+ * <p>
+ * When defining events and the associated extras, it is important to keep their behavior
+ * consistent when the associated {@link ConnectionService} is updated. Support for deprecated
+ * events/extras should me maintained to ensure backwards compatibility with older
+ * {@link InCallService} implementations which were built to support the older behavior.
*
* @param event The connection event.
- * @param extras Bundle containing extra information associated with the event.
- * @hide
+ * @param extras Optional bundle containing extra information associated with the event.
*/
public void sendConnectionEvent(String event, Bundle extras) {
for (Listener l : mListeners) {
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index fc7d741..0c75630 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -556,6 +556,9 @@
case Connection.STATE_DIALING:
mAdapter.setDialing(id);
break;
+ case Connection.STATE_PULLING_CALL:
+ mAdapter.setPulling(id);
+ break;
case Connection.STATE_DISCONNECTED:
// Handled in onDisconnected()
break;
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index c8cd3c0..df7d539 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -143,6 +143,21 @@
}
/**
+ * Sets a call's state to pulling (e.g. a call with {@link Connection#PROPERTY_IS_EXTERNAL_CALL}
+ * is being pulled to the local device.
+ *
+ * @param callId The unique ID of the call whose state is changing to dialing.
+ */
+ void setPulling(String callId) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setPulling(callId);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /**
* Sets a call's state to disconnected.
*
* @param callId The unique ID of the call whose state is changing to disconnected.
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index bf28feb..486f9d5 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -65,6 +65,7 @@
private static final int MSG_REMOVE_EXTRAS = 25;
private static final int MSG_ON_CONNECTION_EVENT = 26;
private static final int MSG_SET_CONNECTION_PROPERTIES = 27;
+ private static final int MSG_SET_PULLING = 28;
private final IConnectionServiceAdapter mDelegate;
@@ -101,6 +102,9 @@
case MSG_SET_DIALING:
mDelegate.setDialing((String) msg.obj);
break;
+ case MSG_SET_PULLING:
+ mDelegate.setPulling((String) msg.obj);
+ break;
case MSG_SET_DISCONNECTED: {
SomeArgs args = (SomeArgs) msg.obj;
try {
@@ -299,6 +303,11 @@
}
@Override
+ public void setPulling(String connectionId) {
+ mHandler.obtainMessage(MSG_SET_PULLING, connectionId).sendToTarget();
+ }
+
+ @Override
public void setDisconnected(
String connectionId, DisconnectCause disconnectCause) {
SomeArgs args = SomeArgs.obtain();
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 65437d9..6860269 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -67,13 +67,11 @@
/**
* Disconnected because the user did not locally answer the incoming call, but it was answered
* on another device where the call was ringing.
- * @hide
*/
public static final int ANSWERED_ELSEWHERE = 11;
/**
* Disconnected because the call was pulled from the current device to another device.
- * @hide
*/
public static final int CALL_PULLED = 12;
@@ -277,6 +275,12 @@
case CONNECTION_MANAGER_NOT_SUPPORTED:
code = "CONNECTION_MANAGER_NOT_SUPPORTED";
break;
+ case CALL_PULLED:
+ code = "CALL_PULLED";
+ break;
+ case ANSWERED_ELSEWHERE:
+ code = "ANSWERED_ELSEWHERE";
+ break;
default:
code = "invalid code: " + mDisconnectCode;
break;
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index e2399ff..69de89d 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -449,15 +449,14 @@
}
/**
- * Called when a {@link Call} has received a connection event issued by the
- * {@link ConnectionService}.
+ * Unused; to handle connection events issued by a {@link ConnectionService}, implement the
+ * {@link android.telecom.Call.Callback#onConnectionEvent(Call, String, Bundle)} callback.
* <p>
* See {@link Connection#sendConnectionEvent(String, Bundle)}.
*
* @param call The call the event is associated with.
* @param event The event.
* @param extras Any associated extras.
- * @hide
*/
public void onConnectionEvent(Call call, String event, Bundle extras) {
}
diff --git a/telecomm/java/android/telecom/ParcelableCallAnalytics.java b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
index e7c9672..383d10b 100644
--- a/telecomm/java/android/telecom/ParcelableCallAnalytics.java
+++ b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
@@ -20,11 +20,231 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
/**
* @hide
*/
@SystemApi
public class ParcelableCallAnalytics implements Parcelable {
+ /** {@hide} */
+ public static final class VideoEvent implements Parcelable {
+ public static final int SEND_LOCAL_SESSION_MODIFY_REQUEST = 0;
+ public static final int SEND_LOCAL_SESSION_MODIFY_RESPONSE = 1;
+ public static final int RECEIVE_REMOTE_SESSION_MODIFY_REQUEST = 2;
+ public static final int RECEIVE_REMOTE_SESSION_MODIFY_RESPONSE = 3;
+
+ public static final Parcelable.Creator<VideoEvent> CREATOR =
+ new Parcelable.Creator<VideoEvent> () {
+
+ @Override
+ public VideoEvent createFromParcel(Parcel in) {
+ return new VideoEvent(in);
+ }
+
+ @Override
+ public VideoEvent[] newArray(int size) {
+ return new VideoEvent[size];
+ }
+ };
+
+ private int mEventName;
+ private long mTimeSinceLastEvent;
+ private int mVideoState;
+
+ public VideoEvent(int eventName, long timeSinceLastEvent, int videoState) {
+ mEventName = eventName;
+ mTimeSinceLastEvent = timeSinceLastEvent;
+ mVideoState = videoState;
+ }
+
+ VideoEvent(Parcel in) {
+ mEventName = in.readInt();
+ mTimeSinceLastEvent = in.readLong();
+ mVideoState = in.readInt();
+ }
+
+ public int getEventName() {
+ return mEventName;
+ }
+
+ public long getTimeSinceLastEvent() {
+ return mTimeSinceLastEvent;
+ }
+
+ public int getVideoState() {
+ return mVideoState;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mEventName);
+ out.writeLong(mTimeSinceLastEvent);
+ out.writeInt(mVideoState);
+ }
+ }
+
+ public static final class AnalyticsEvent implements Parcelable {
+ public static final int SET_SELECT_PHONE_ACCOUNT = 0;
+ public static final int SET_ACTIVE = 1;
+ public static final int SET_DISCONNECTED = 2;
+ public static final int START_CONNECTION = 3;
+ public static final int SET_DIALING = 4;
+ public static final int BIND_CS = 5;
+ public static final int CS_BOUND = 6;
+ public static final int REQUEST_ACCEPT = 7;
+ public static final int REQUEST_REJECT = 8;
+
+ public static final int SCREENING_SENT = 100;
+ public static final int SCREENING_COMPLETED = 101;
+ public static final int DIRECT_TO_VM_INITIATED = 102;
+ public static final int DIRECT_TO_VM_FINISHED = 103;
+ public static final int BLOCK_CHECK_INITIATED = 104;
+ public static final int BLOCK_CHECK_FINISHED = 105;
+ public static final int FILTERING_INITIATED = 106;
+ public static final int FILTERING_COMPLETED = 107;
+ public static final int FILTERING_TIMED_OUT = 108;
+
+ public static final int SKIP_RINGING = 200;
+ public static final int SILENCE = 201;
+ public static final int MUTE = 202;
+ public static final int UNMUTE = 203;
+ public static final int AUDIO_ROUTE_BT = 204;
+ public static final int AUDIO_ROUTE_EARPIECE = 205;
+ public static final int AUDIO_ROUTE_HEADSET = 206;
+ public static final int AUDIO_ROUTE_SPEAKER = 207;
+
+ public static final int CONFERENCE_WITH = 300;
+ public static final int SPLIT_CONFERENCE = 301;
+ public static final int SET_PARENT = 302;
+
+ public static final int REQUEST_HOLD = 400;
+ public static final int REQUEST_UNHOLD = 401;
+ public static final int REMOTELY_HELD = 402;
+ public static final int REMOTELY_UNHELD = 403;
+ public static final int SET_HOLD = 404;
+ public static final int SWAP = 405;
+
+ public static final int REQUEST_PULL = 500;
+
+
+ public static final Parcelable.Creator<AnalyticsEvent> CREATOR =
+ new Parcelable.Creator<AnalyticsEvent> () {
+
+ @Override
+ public AnalyticsEvent createFromParcel(Parcel in) {
+ return new AnalyticsEvent(in);
+ }
+
+ @Override
+ public AnalyticsEvent[] newArray(int size) {
+ return new AnalyticsEvent[size];
+ }
+ };
+
+ private int mEventName;
+ private long mTimeSinceLastEvent;
+
+ public AnalyticsEvent(int eventName, long timestamp) {
+ mEventName = eventName;
+ mTimeSinceLastEvent = timestamp;
+ }
+
+ AnalyticsEvent(Parcel in) {
+ mEventName = in.readInt();
+ mTimeSinceLastEvent = in.readLong();
+ }
+
+ public int getEventName() {
+ return mEventName;
+ }
+
+ public long getTimeSinceLastEvent() {
+ return mTimeSinceLastEvent;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mEventName);
+ out.writeLong(mTimeSinceLastEvent);
+ }
+ }
+
+ public static final class EventTiming implements Parcelable {
+ public static final int ACCEPT_TIMING = 0;
+ public static final int REJECT_TIMING = 1;
+ public static final int DISCONNECT_TIMING = 2;
+ public static final int HOLD_TIMING = 3;
+ public static final int UNHOLD_TIMING = 4;
+ public static final int OUTGOING_TIME_TO_DIALING_TIMING = 5;
+ public static final int BIND_CS_TIMING = 6;
+ public static final int SCREENING_COMPLETED_TIMING = 7;
+ public static final int DIRECT_TO_VM_FINISHED_TIMING = 8;
+ public static final int BLOCK_CHECK_FINISHED_TIMING = 9;
+ public static final int FILTERING_COMPLETED_TIMING = 10;
+ public static final int FILTERING_TIMED_OUT_TIMING = 11;
+
+ public static final int INVALID = 999999;
+
+ public static final Parcelable.Creator<EventTiming> CREATOR =
+ new Parcelable.Creator<EventTiming> () {
+
+ @Override
+ public EventTiming createFromParcel(Parcel in) {
+ return new EventTiming(in);
+ }
+
+ @Override
+ public EventTiming[] newArray(int size) {
+ return new EventTiming[size];
+ }
+ };
+
+ private int mName;
+ private long mTime;
+
+ public EventTiming(int name, long time) {
+ this.mName = name;
+ this.mTime = time;
+ }
+
+ private EventTiming(Parcel in) {
+ mName = in.readInt();
+ mTime = in.readLong();
+ }
+
+ public int getName() {
+ return mName;
+ }
+
+ public long getTime() {
+ return mTime;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mName);
+ out.writeLong(mTime);
+ }
+ }
+
public static final int CALLTYPE_UNKNOWN = 0;
public static final int CALLTYPE_INCOMING = 1;
public static final int CALLTYPE_OUTGOING = 2;
@@ -87,10 +307,23 @@
// Whether the call object was created from an existing connection.
private final boolean isCreatedFromExistingConnection;
+ // A list of events that are associated with this call
+ private final List<AnalyticsEvent> analyticsEvents;
+
+ // A map from event-pair names to their durations.
+ private final List<EventTiming> eventTimings;
+
+ // Whether the call has ever been a video call.
+ private boolean isVideoCall = false;
+
+ // A list of video events that have occurred.
+ private List<VideoEvent> videoEvents;
+
public ParcelableCallAnalytics(long startTimeMillis, long callDurationMillis, int callType,
boolean isAdditionalCall, boolean isInterrupted, int callTechnologies,
int callTerminationCode, boolean isEmergencyCall, String connectionService,
- boolean isCreatedFromExistingConnection) {
+ boolean isCreatedFromExistingConnection, List<AnalyticsEvent> analyticsEvents,
+ List<EventTiming> eventTimings) {
this.startTimeMillis = startTimeMillis;
this.callDurationMillis = callDurationMillis;
this.callType = callType;
@@ -101,6 +334,8 @@
this.isEmergencyCall = isEmergencyCall;
this.connectionService = connectionService;
this.isCreatedFromExistingConnection = isCreatedFromExistingConnection;
+ this.analyticsEvents = analyticsEvents;
+ this.eventTimings = eventTimings;
}
public ParcelableCallAnalytics(Parcel in) {
@@ -114,6 +349,13 @@
isEmergencyCall = readByteAsBoolean(in);
connectionService = in.readString();
isCreatedFromExistingConnection = readByteAsBoolean(in);
+ analyticsEvents = new ArrayList<>();
+ in.readTypedList(analyticsEvents, AnalyticsEvent.CREATOR);
+ eventTimings = new ArrayList<>();
+ in.readTypedList(eventTimings, EventTiming.CREATOR);
+ isVideoCall = readByteAsBoolean(in);
+ videoEvents = new LinkedList<>();
+ in.readTypedList(videoEvents, VideoEvent.CREATOR);
}
public void writeToParcel(Parcel out, int flags) {
@@ -127,6 +369,20 @@
writeBooleanAsByte(out, isEmergencyCall);
out.writeString(connectionService);
writeBooleanAsByte(out, isCreatedFromExistingConnection);
+ out.writeTypedList(analyticsEvents);
+ out.writeTypedList(eventTimings);
+ writeBooleanAsByte(out, isVideoCall);
+ out.writeTypedList(videoEvents);
+ }
+
+ /** {@hide} */
+ public void setIsVideoCall(boolean isVideoCall) {
+ this.isVideoCall = isVideoCall;
+ }
+
+ /** {@hide} */
+ public void setVideoEvents(List<VideoEvent> videoEvents) {
+ this.videoEvents = videoEvents;
}
public long getStartTimeMillis() {
@@ -169,6 +425,24 @@
return isCreatedFromExistingConnection;
}
+ public List<AnalyticsEvent> analyticsEvents() {
+ return analyticsEvents;
+ }
+
+ public List<EventTiming> getEventTimings() {
+ return eventTimings;
+ }
+
+ /** {@hide} */
+ public boolean isVideoCall() {
+ return isVideoCall;
+ }
+
+ /** {@hide} */
+ public List<VideoEvent> getVideoEvents() {
+ return videoEvents;
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index dbc2b0c..473e394 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -220,6 +220,7 @@
private final Icon mIcon;
private final Bundle mExtras;
private boolean mIsEnabled;
+ private String mGroupId;
/**
* Helper class for creating a {@link PhoneAccount}.
@@ -236,6 +237,7 @@
private Icon mIcon;
private Bundle mExtras;
private boolean mIsEnabled = false;
+ private String mGroupId = "";
/**
* Creates a builder with the specified {@link PhoneAccountHandle} and label.
@@ -263,6 +265,7 @@
mIcon = phoneAccount.getIcon();
mIsEnabled = phoneAccount.isEnabled();
mExtras = phoneAccount.getExtras();
+ mGroupId = phoneAccount.getGroupId();
}
/**
@@ -387,6 +390,27 @@
}
/**
+ * Sets the group Id of the {@link PhoneAccount}. When a new {@link PhoneAccount} is
+ * registered to Telecom, it will replace another {@link PhoneAccount} that is already
+ * registered in Telecom and take on the current user defaults and enabled status. There can
+ * only be one {@link PhoneAccount} with a non-empty group number registered to Telecom at a
+ * time. By default, there is no group Id for a {@link PhoneAccount} (an empty String). Only
+ * grouped {@link PhoneAccount}s with the same {@link ConnectionService} can be replaced.
+ * @param groupId The group Id of the {@link PhoneAccount} that will replace any other
+ * registered {@link PhoneAccount} in Telecom with the same Group Id.
+ * @return The builder
+ * @hide
+ */
+ public Builder setGroupId(String groupId) {
+ if (groupId != null) {
+ mGroupId = groupId;
+ } else {
+ mGroupId = "";
+ }
+ return this;
+ }
+
+ /**
* Creates an instance of a {@link PhoneAccount} based on the current builder settings.
*
* @return The {@link PhoneAccount}.
@@ -408,7 +432,8 @@
mShortDescription,
mSupportedUriSchemes,
mExtras,
- mIsEnabled);
+ mIsEnabled,
+ mGroupId);
}
}
@@ -423,7 +448,8 @@
CharSequence shortDescription,
List<String> supportedUriSchemes,
Bundle extras,
- boolean isEnabled) {
+ boolean isEnabled,
+ String groupId) {
mAccountHandle = account;
mAddress = address;
mSubscriptionAddress = subscriptionAddress;
@@ -435,6 +461,7 @@
mSupportedUriSchemes = Collections.unmodifiableList(supportedUriSchemes);
mExtras = extras;
mIsEnabled = isEnabled;
+ mGroupId = groupId;
}
public static Builder builder(
@@ -564,6 +591,21 @@
}
/**
+ * A non-empty {@link String} representing the group that A {@link PhoneAccount} is in or an
+ * empty {@link String} if the {@link PhoneAccount} is not in a group. If this
+ * {@link PhoneAccount} is in a group, this new {@link PhoneAccount} will replace a registered
+ * {@link PhoneAccount} that is in the same group. When the {@link PhoneAccount} is replaced,
+ * its user defined defaults and enabled status will also pass to this new {@link PhoneAccount}.
+ * Only {@link PhoneAccount}s that share the same {@link ConnectionService} can be replaced.
+ *
+ * @return A non-empty String Id if this {@link PhoneAccount} belongs to a group.
+ * @hide
+ */
+ public String getGroupId() {
+ return mGroupId;
+ }
+
+ /**
* Determines if the {@link PhoneAccount} supports calls to/from addresses with a specified URI
* scheme.
*
@@ -644,6 +686,7 @@
}
out.writeByte((byte) (mIsEnabled ? 1 : 0));
out.writeBundle(mExtras);
+ out.writeString(mGroupId);
}
public static final Creator<PhoneAccount> CREATOR
@@ -687,6 +730,7 @@
}
mIsEnabled = in.readByte() == 1;
mExtras = in.readBundle();
+ mGroupId = in.readString();
}
@Override
@@ -704,6 +748,8 @@
}
sb.append(" Extras: ");
sb.append(mExtras);
+ sb.append(" GroupId: ");
+ sb.append(Log.pii(mGroupId));
sb.append("]");
return sb.toString();
}
diff --git a/telecomm/java/android/telecom/RemoteConference.java b/telecomm/java/android/telecom/RemoteConference.java
index bf6038a..943da6d 100644
--- a/telecomm/java/android/telecom/RemoteConference.java
+++ b/telecomm/java/android/telecom/RemoteConference.java
@@ -97,7 +97,6 @@
*
* @param conference The {@code RemoteConference} invoking this method.
* @param connectionProperties The new properties of the {@code RemoteConference}.
- * @hide
*/
public void onConnectionPropertiesChanged(
RemoteConference conference,
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 8e06659db..dc8eaf6 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -95,7 +95,6 @@
*
* @param connection The {@code RemoteConnection} invoking this method.
* @param connectionProperties The new properties of the {@code RemoteConnection}.
- * @hide
*/
public void onConnectionPropertiesChanged(
RemoteConnection connection,
@@ -230,7 +229,6 @@
* @param connection The {@code RemoteConnection} invoking this method.
* @param event The connection event.
* @param extras Extras associated with the event.
- * @hide
*/
public void onConnectionEvent(RemoteConnection connection, String event, Bundle extras) {}
}
@@ -738,7 +736,6 @@
*
* @return A bitmask of the properties of the {@code RemoteConnection}, as defined in the
* {@code PROPERTY_*} constants in class {@link Connection}.
- * @hide
*/
public int getConnectionProperties() {
return mConnectionProperties;
@@ -993,7 +990,6 @@
* Instructs this {@link RemoteConnection} to pull itself to the local device.
* <p>
* See {@link Call#pullExternalCall()} for more information.
- * @hide
*/
public void pullExternalCall() {
try {
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 21a7706..c4739ff 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -118,6 +118,12 @@
}
@Override
+ public void setPulling(String callId) {
+ findConnectionForAction(callId, "setPulling")
+ .setState(Connection.STATE_PULLING_CALL);
+ }
+
+ @Override
public void setDisconnected(String callId, DisconnectCause disconnectCause) {
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "setDisconnected")
@@ -218,6 +224,7 @@
conference.setState(parcel.getState());
conference.setConnectionCapabilities(parcel.getConnectionCapabilities());
+ conference.setConnectionProperties(parcel.getConnectionProperties());
mConferenceById.put(callId, conference);
conference.registerCallback(new RemoteConference.Callback() {
@Override
diff --git a/telecomm/java/android/telecom/ParcelableCallAnalytics.aidl b/telecomm/java/android/telecom/TelecomAnalytics.aidl
similarity index 94%
rename from telecomm/java/android/telecom/ParcelableCallAnalytics.aidl
rename to telecomm/java/android/telecom/TelecomAnalytics.aidl
index b7e78d1..08ad0a2 100644
--- a/telecomm/java/android/telecom/ParcelableCallAnalytics.aidl
+++ b/telecomm/java/android/telecom/TelecomAnalytics.aidl
@@ -19,4 +19,4 @@
/**
* {@hide}
*/
-parcelable ParcelableCallAnalytics;
+parcelable TelecomAnalytics;
diff --git a/telecomm/java/android/telecom/TelecomAnalytics.java b/telecomm/java/android/telecom/TelecomAnalytics.java
new file mode 100644
index 0000000..6e0d02c
--- /dev/null
+++ b/telecomm/java/android/telecom/TelecomAnalytics.java
@@ -0,0 +1,148 @@
+/*
+ * 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.telecom;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @hide
+ */
+@SystemApi
+public final class TelecomAnalytics implements Parcelable {
+ public static final Parcelable.Creator<TelecomAnalytics> CREATOR =
+ new Parcelable.Creator<TelecomAnalytics> () {
+
+ @Override
+ public TelecomAnalytics createFromParcel(Parcel in) {
+ return new TelecomAnalytics(in);
+ }
+
+ @Override
+ public TelecomAnalytics[] newArray(int size) {
+ return new TelecomAnalytics[size];
+ }
+ };
+
+ public static final class SessionTiming extends TimedEvent<Integer> implements Parcelable {
+ public static final Parcelable.Creator<SessionTiming> CREATOR =
+ new Parcelable.Creator<SessionTiming> () {
+
+ @Override
+ public SessionTiming createFromParcel(Parcel in) {
+ return new SessionTiming(in);
+ }
+
+ @Override
+ public SessionTiming[] newArray(int size) {
+ return new SessionTiming[size];
+ }
+ };
+
+ public static final int ICA_ANSWER_CALL = 1;
+ public static final int ICA_REJECT_CALL = 2;
+ public static final int ICA_DISCONNECT_CALL = 3;
+ public static final int ICA_HOLD_CALL = 4;
+ public static final int ICA_UNHOLD_CALL = 5;
+ public static final int ICA_MUTE = 6;
+ public static final int ICA_SET_AUDIO_ROUTE = 7;
+ public static final int ICA_CONFERENCE = 8;
+
+ public static final int CSW_HANDLE_CREATE_CONNECTION_COMPLETE = 100;
+ public static final int CSW_SET_ACTIVE = 101;
+ public static final int CSW_SET_RINGING = 102;
+ public static final int CSW_SET_DIALING = 103;
+ public static final int CSW_SET_DISCONNECTED = 104;
+ public static final int CSW_SET_ON_HOLD = 105;
+ public static final int CSW_REMOVE_CALL = 106;
+ public static final int CSW_SET_IS_CONFERENCED = 107;
+ public static final int CSW_ADD_CONFERENCE_CALL = 108;
+
+ private int mId;
+ private long mTime;
+
+ public SessionTiming(int id, long time) {
+ this.mId = id;
+ this.mTime = time;
+ }
+
+ private SessionTiming(Parcel in) {
+ mId = in.readInt();
+ mTime = in.readLong();
+ }
+
+ @Override
+ public Integer getKey() {
+ return mId;
+ }
+
+ @Override
+ public long getTime() {
+ return mTime;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mId);
+ out.writeLong(mTime);
+ }
+ }
+
+ private List<SessionTiming> mSessionTimings;
+ private List<ParcelableCallAnalytics> mCallAnalytics;
+
+ public TelecomAnalytics(List<SessionTiming> sessionTimings,
+ List<ParcelableCallAnalytics> callAnalytics) {
+ this.mSessionTimings = sessionTimings;
+ this.mCallAnalytics = callAnalytics;
+ }
+
+ private TelecomAnalytics(Parcel in) {
+ mSessionTimings = new ArrayList<>();
+ in.readTypedList(mSessionTimings, SessionTiming.CREATOR);
+ mCallAnalytics = new ArrayList<>();
+ in.readTypedList(mCallAnalytics, ParcelableCallAnalytics.CREATOR);
+ }
+
+ public List<SessionTiming> getSessionTimings() {
+ return mSessionTimings;
+ }
+
+ public List<ParcelableCallAnalytics> getCallAnalytics() {
+ return mCallAnalytics;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeTypedList(mSessionTimings);
+ out.writeTypedList(mCallAnalytics);
+ }
+}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index da0d048..f12886a 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -348,7 +348,6 @@
* informed of external calls should set this meta-data to {@code true} in the manifest
* registration of their {@link InCallService}. By default, the {@link InCallService} will NOT
* be informed of external calls.
- * @hide
*/
public static final String METADATA_INCLUDE_EXTERNAL_CALLS =
"android.telecom.INCLUDE_EXTERNAL_CALLS";
@@ -1444,9 +1443,9 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.DUMP)
- public List<ParcelableCallAnalytics> dumpAnalytics() {
+ public TelecomAnalytics dumpAnalytics() {
ITelecomService service = getTelecomService();
- List<ParcelableCallAnalytics> result = null;
+ TelecomAnalytics result = null;
if (service != null) {
try {
result = service.dumpCallAnalytics();
diff --git a/telecomm/java/android/telecom/TimedEvent.java b/telecomm/java/android/telecom/TimedEvent.java
new file mode 100644
index 0000000..e484e79
--- /dev/null
+++ b/telecomm/java/android/telecom/TimedEvent.java
@@ -0,0 +1,51 @@
+/*
+ * 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.telecom;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @hide
+ */
+public abstract class TimedEvent<T> {
+ public abstract long getTime();
+ public abstract T getKey();
+
+ public static <T> Map<T, Double> averageTimings(Collection<? extends TimedEvent<T>> events) {
+ HashMap<T, Integer> counts = new HashMap<>();
+ HashMap<T, Double> result = new HashMap<>();
+
+ for (TimedEvent<T> entry : events) {
+ if (counts.containsKey(entry.getKey())) {
+ counts.put(entry.getKey(), counts.get(entry.getKey()) + 1);
+ result.put(entry.getKey(), result.get(entry.getKey()) + entry.getTime());
+ } else {
+ counts.put(entry.getKey(), 1);
+ result.put(entry.getKey(), (double) entry.getTime());
+ }
+ }
+
+ for (Map.Entry<T, Double> entry : result.entrySet()) {
+ result.put(entry.getKey(), entry.getValue() / counts.get(entry.getKey()));
+ }
+
+ return result;
+ }
+}
+
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index 9bc8ffe..3bf83ae 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -47,6 +47,8 @@
void setDialing(String callId);
+ void setPulling(String callId);
+
void setDisconnected(String callId, in DisconnectCause disconnectCause);
void setOnHold(String callId);
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 871565d..5c412e7 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -18,7 +18,7 @@
import android.content.ComponentName;
import android.content.Intent;
-import android.telecom.ParcelableCallAnalytics;
+import android.telecom.TelecomAnalytics;
import android.telecom.PhoneAccountHandle;
import android.net.Uri;
import android.os.Bundle;
@@ -148,7 +148,7 @@
/**
* @see TelecomServiceImpl#dumpCallAnalytics
*/
- List<ParcelableCallAnalytics> dumpCallAnalytics();
+ TelecomAnalytics dumpCallAnalytics();
//
// Internal system apis relating to call management.
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ff7ca62..dc9767c 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -24,6 +24,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
+import com.android.ims.ImsReasonInfo;
import com.android.internal.telephony.ICarrierConfigLoader;
/**
@@ -245,6 +246,29 @@
public static final String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
/**
+ * Flag specifying whether the carrier wants to notify the user when a VT call has been handed
+ * over from WIFI to LTE.
+ * <p>
+ * The handover notification is sent as a
+ * {@link TelephonyManager#EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE}
+ * {@link android.telecom.Connection} event, which an {@link android.telecom.InCallService}
+ * should use to trigger the display of a user-facing message.
+ * <p>
+ * The Connection event is sent to the InCallService only once, the first time it occurs.
+ * @hide
+ */
+ public static final String KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL =
+ "notify_handover_video_from_wifi_to_lte_bool";
+
+ /**
+ * Flag specifying whether the carrier supports downgrading a video call (tx, rx or tx/rx)
+ * directly to an audio call.
+ * @hide
+ */
+ public static final String KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL =
+ "support_downgrade_vt_to_audio_bool";
+
+ /**
* Flag specifying whether WFC over IMS should be available for carrier: independent of
* carrier provisioning. If false: hard disabled. If true: then depends on carrier
* provisioning, availability etc.
@@ -285,6 +309,16 @@
public static final String KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL =
"carrier_default_wfc_ims_roaming_enabled_bool";
+ /**
+ * Flag indicating whether failed calls due to no service should prompt the user to enable
+ * WIFI calling. When {@code true}, if the user attempts to establish a call when there is no
+ * service available, they are connected to WIFI, and WIFI calling is disabled, a different
+ * call failure message will be used to encourage the user to enable WIFI calling.
+ * @hide
+ */
+ public static final String KEY_CARRIER_PROMOTE_WFC_ON_CALL_FAIL_BOOL =
+ "carrier_promote_wfc_on_call_fail_bool";
+
/** Flag specifying whether provisioning is required for VOLTE. */
public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
= "carrier_volte_provisioning_required_bool";
@@ -392,6 +426,13 @@
"always_show_emergency_alert_onoff_bool";
/**
+ * The flag to disable cell broadcast severe alert when extreme alert is disabled.
+ * @hide
+ */
+ public static final String KEY_DISABLE_SEVERE_WHEN_EXTREME_DISABLED_BOOL =
+ "disable_severe_when_extreme_disabled_bool";
+
+ /**
* The data call APN retry configuration for default type APN.
* @hide
*/
@@ -530,6 +571,17 @@
public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
/**
+ * Determines whether video conference calls are supported by a carrier. When {@code true},
+ * video calls can be merged into conference calls, {@code false} otherwiwse.
+ * <p>
+ * Note: even if video conference calls are not supported, audio calls may be merged into a
+ * conference if {@link #KEY_SUPPORT_CONFERENCE_CALL_BOOL} is {@code true}.
+ * @hide
+ */
+ public static final String KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL =
+ "support_video_conference_call_bool";
+
+ /**
* Determine whether user can toggle Enhanced 4G LTE Mode in Settings.
*/
public static final String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
@@ -582,6 +634,14 @@
public static final String KEY_WFC_DATA_SPN_FORMAT_IDX_INT = "wfc_data_spn_format_idx_int";
/**
+ * The Component Name of the activity that can setup the emergency addrees for WiFi Calling
+ * as per carrier requirement.
+ * @hide
+ */
+ public static final String KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING =
+ "wfc_emergency_address_carrier_app_string";
+
+ /**
* Boolean to decide whether to use #KEY_CARRIER_NAME_STRING from CarrierConfig app.
* @hide
*/
@@ -594,7 +654,6 @@
*/
public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
-
/**
* If this is true, the SIM card (through Customer Service Profile EF file) will be able to
* prevent manual operator selection. If false, this SIM setting will be ignored and manual
@@ -615,6 +674,13 @@
public static final String KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL =
"broadcast_emergency_call_state_changes_bool";
+ /**
+ * Cell broadcast additional channels enbled by the carrier
+ * @hide
+ */
+ public static final String KEY_CARRIER_ADDITIONAL_CBS_CHANNELS_STRINGS =
+ "carrier_additional_cbs_channels_strings";
+
// These variables are used by the MMS service and exposed through another API, {@link
// SmsManager}. The variable names and string values are copied from there.
public static final String KEY_MMS_ALIAS_ENABLED_BOOL = "aliasEnabled";
@@ -648,6 +714,8 @@
public static final String KEY_MMS_UA_PROF_TAG_NAME_STRING = "uaProfTagName";
public static final String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
public static final String KEY_MMS_USER_AGENT_STRING = "userAgent";
+ /** @hide */
+ public static final String KEY_MMS_CLOSE_CONNECTION_BOOL = "mmsCloseConnection";
/**
* If carriers require differentiate un-provisioned status: cold sim or out of credit sim
@@ -658,10 +726,44 @@
* example:
* <item>com.google.android.carrierPackageName</item>
* <item>com.google.android.carrierPackageName.CarrierActivityName</item>
+ * The ComponentName of the carrier activity that can setup the device and activate with the
+ * network as part of the Setup Wizard flow.
* @hide
*/
- public static final String KEY_SIM_PROVISIONING_STATUS_DETECTION_CARRIER_APP_STRING_ARRAY =
- "sim_state_detection_carrier_app_string_array";
+ public static final String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
+
+ /**
+ * A list of component name of carrier signalling receivers which are interested in intent
+ * android.intent.action.CARRIER_SIGNAL_REDIRECTED.
+ * Example:
+ * <item>com.google.android.carrierPackageName/.CarrierSignalReceiverNameA</item>
+ * <item>com.google.android.carrierPackageName/.CarrierSignalReceiverNameB</item>
+ * @hide
+ */
+ public static final String KEY_SIGNAL_REDIRECTION_RECEIVER_STRING_ARRAY =
+ "signal_redirection_receiver_string_array";
+
+ /**
+ * A list of component name of carrier signalling receivers which are interested in intent
+ * android.intent.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED.
+ * Example:
+ * <item>com.google.android.carrierPackageName/.CarrierSignalReceiverNameA</item>
+ * <item>com.google.android.carrierPackageName/.CarrierSignalReceiverNameB</item>
+ * @hide
+ */
+ public static final String KEY_SIGNAL_DCFAILURE_RECEIVER_STRING_ARRAY =
+ "signal_dcfailure_receiver_string_array";
+
+ /**
+ * A list of component name of carrier signalling receivers which are interested in intent
+ * android.intent.action.CARRIER_SIGNAL_PCO_VALUE.
+ * Example:
+ * <item>com.google.android.carrierPackageName/.CarrierSignalReceiverNameA</item>
+ * <item>com.google.android.carrierPackageName/.CarrierSignalReceiverNameB</item>
+ * @hide
+ */
+ public static final String KEY_SIGNAL_PCO_RECEIVER_STRING_ARRAY =
+ "signal_pco_receiver_string_array";
/**
* Determines whether the carrier supports making non-emergency phone calls while the phone is
@@ -718,11 +820,137 @@
/** @hide */
public static final int CDMA_ROAMING_MODE_ANY = 2;
+ /**
+ * Report IMEI as device id even if it's a CDMA/LTE phone.
+ *
+ * @hide
+ */
+ public static final String KEY_FORCE_IMEI_BOOL = "force_imei_bool";
+
+ /**
+ * The families of Radio Access Technologies that will get clustered and ratcheted,
+ * ie, we will report transitions up within the family, but not down until we change
+ * cells. This prevents flapping between base technologies and higher techs that are
+ * granted on demand within the cell.
+ * @hide
+ */
+ public static final String KEY_RATCHET_RAT_FAMILIES =
+ "ratchet_rat_families";
+
+ /**
+ * Flag indicating whether some telephony logic will treat a call which was formerly a video
+ * call as if it is still a video call. When {@code true}:
+ * <p>
+ * Logic which will automatically drop a video call which takes place over WIFI when a
+ * voice call is answered (see {@link #KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL}.
+ * <p>
+ * Logic which determines whether the user can use TTY calling.
+ */
+ public static final String KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL =
+ "treat_downgraded_video_calls_as_video_calls_bool";
+
+ /**
+ * When {@code true}, if the user is in an ongoing video call over WIFI and answers an incoming
+ * audio call, the video call will be disconnected before the audio call is answered. This is
+ * in contrast to the usual expected behavior where a foreground video call would be put into
+ * the background and held when an incoming audio call is answered.
+ */
+ public static final String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL =
+ "drop_video_call_when_answering_audio_call_bool";
+
+ /**
+ * Flag indicating whether the carrier supports merging wifi calls when VoWIFI is disabled.
+ * This can happen in the case of a carrier which allows offloading video calls to WIFI
+ * separately of whether voice over wifi is enabled. In such a scenario when two video calls
+ * are downgraded to voice, they remain over wifi. However, if VoWIFI is disabled, these calls
+ * cannot be merged.
+ */
+ public static final String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL =
+ "allow_merge_wifi_calls_when_vowifi_off_bool";
+
+ /**
+ * Flag indicating whether the carrier supports the Hold command while in an IMS call.
+ * @hide
+ */
+ public static final String KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL = "allow_hold_in_ims_call";
+
+ /**
+ * When true, indicates that adding a call is disabled when there is an ongoing video call
+ * or when there is an ongoing call on wifi which was downgraded from video and VoWifi is
+ * turned off.
+ */
+ public static final String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL =
+ "allow_add_call_during_video_call";
+
+ /**
+ * When true, indicates that the HD audio icon in the in-call screen should not be shown for
+ * VoWifi calls.
+ * @hide
+ */
+ public static final String KEY_WIFI_CALLS_CAN_BE_HD_AUDIO = "wifi_calls_can_be_hd_audio";
+
+ /**
+ * When true, indicates that the HD audio icon in the in-call screen should not be shown for
+ * video calls.
+ * @hide
+ */
+ public static final String KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO = "video_calls_can_be_hd_audio";
+
+ /**
+ * Defines operator-specific {@link com.android.ims.ImsReasonInfo} mappings.
+ *
+ * Format: "ORIGINAL_CODE|MESSAGE|NEW_CODE"
+ * Where {@code ORIGINAL_CODE} corresponds to a {@link ImsReasonInfo#getCode()} code,
+ * {@code MESSAGE} corresponds to an expected {@link ImsReasonInfo#getExtraMessage()} string,
+ * and {@code NEW_CODE} is the new {@code ImsReasonInfo#CODE_*} which this combination of
+ * original code and message shall be remapped to.
+ *
+ * Example: "501|call completion elsewhere|1014"
+ * When the {@link ImsReasonInfo#getCode()} is {@link ImsReasonInfo#CODE_USER_TERMINATED} and
+ * the {@link ImsReasonInfo#getExtraMessage()} is {@code "call completion elsewhere"},
+ * {@link ImsReasonInfo#CODE_ANSWERED_ELSEWHERE} shall be used as the {@link ImsReasonInfo}
+ * code instead.
+ * @hide
+ */
+ public static final String KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY =
+ "ims_reasoninfo_mapping_string_array";
+
+ /**
+ * When {@code false}, use default title for Enhanced 4G LTE Mode settings.
+ * When {@code true}, use the variant.
+ * @hide
+ */
+ public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL =
+ "enhanced_4g_lte_title_variant_bool";
+
+ /**
+ * Indicates whether the carrier wants to notify the user when handover of an LTE video call to
+ * WIFI fails.
+ * <p>
+ * When {@code true}, if a video call starts on LTE and the modem reports a failure to handover
+ * the call to WIFI or if no handover success is reported within 60 seconds of call initiation,
+ * the {@link android.telephony.TelephonyManager#EVENT_HANDOVER_TO_WIFI_FAILED} event is raised
+ * on the connection.
+ * @hide
+ */
+ public static final String KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL =
+ "notify_vt_handover_to_wifi_failure_bool";
+
+ /**
+ * A upper case list of CNAP names that are unhelpful to the user for distinguising calls and
+ * should be filtered out of the CNAP information. This includes CNAP names such as "WIRELESS
+ * CALLER" or "UNKNOWN NAME". By default, if there are no filtered names for this carrier, null
+ * is returned.
+ * @hide
+ */
+ public static final String FILTERED_CNAP_NAMES_STRING_ARRAY = "filtered_cnap_names_string_array";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
static {
sDefaults = new PersistableBundle();
+ sDefaults.putBoolean(KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL, true);
sDefaults.putBoolean(KEY_ADDITIONAL_CALL_SETTING_BOOL, true);
sDefaults.putBoolean(KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL, false);
sDefaults.putBoolean(KEY_ALLOW_LOCAL_DTMF_TONES_BOOL, true);
@@ -731,10 +959,13 @@
sDefaults.putBoolean(KEY_CARRIER_SETTINGS_ENABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VT_AVAILABLE_BOOL, false);
+ sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL, false);
+ sDefaults.putBoolean(KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_PROMOTE_WFC_ON_CALL_FAIL_BOOL, false);
sDefaults.putInt(KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT, 2);
sDefaults.putBoolean(KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
@@ -785,6 +1016,7 @@
sDefaults.putBoolean(KEY_ALLOW_ADDING_APNS_BOOL, true);
sDefaults.putBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
+ sDefaults.putBoolean(KEY_DISABLE_SEVERE_WHEN_EXTREME_DISABLED_BOOL, true);
sDefaults.putString(KEY_CARRIER_DATA_CALL_RETRY_CONFIG_DEFAULT_STRING,
"default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,"
+ "320000:5000,640000:5000,1280000:5000,1800000:5000");
@@ -808,6 +1040,7 @@
sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0);
sDefaults.putInt(KEY_CDMA_DTMF_TONE_DELAY_INT, 100);
sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
+ sDefaults.putBoolean(KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL, false);
sDefaults.putBoolean(KEY_EDITABLE_ENHANCED_4G_LTE_BOOL, true);
sDefaults.putBoolean(KEY_HIDE_IMS_APN_BOOL, false);
sDefaults.putBoolean(KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, false);
@@ -816,6 +1049,7 @@
sDefaults.putStringArray(KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY, null);
sDefaults.putInt(KEY_WFC_SPN_FORMAT_IDX_INT, 0);
sDefaults.putInt(KEY_WFC_DATA_SPN_FORMAT_IDX_INT, 0);
+ sDefaults.putString(KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING, "");
sDefaults.putBoolean(KEY_CONFIG_WIFI_DISABLE_IN_ECBM, false);
sDefaults.putBoolean(KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
sDefaults.putString(KEY_CARRIER_NAME_STRING, "");
@@ -835,6 +1069,7 @@
sDefaults.putBoolean(KEY_MMS_SMS_DELIVERY_REPORT_ENABLED_BOOL, true);
sDefaults.putBoolean(KEY_MMS_SUPPORT_HTTP_CHARSET_HEADER_BOOL, false);
sDefaults.putBoolean(KEY_MMS_SUPPORT_MMS_CONTENT_DISPOSITION_BOOL, true);
+ sDefaults.putBoolean(KEY_MMS_CLOSE_CONNECTION_BOOL, false);
sDefaults.putInt(KEY_MMS_ALIAS_MAX_CHARS_INT, 48);
sDefaults.putInt(KEY_MMS_ALIAS_MIN_CHARS_INT, 2);
sDefaults.putInt(KEY_MMS_HTTP_SOCKET_TIMEOUT_INT, 60 * 1000);
@@ -854,11 +1089,31 @@
sDefaults.putString(KEY_MMS_USER_AGENT_STRING, "");
sDefaults.putBoolean(KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL, true);
sDefaults.putBoolean(KEY_USE_RCS_PRESENCE_BOOL, false);
+ sDefaults.putBoolean(KEY_FORCE_IMEI_BOOL, false);
sDefaults.putInt(KEY_CDMA_ROAMING_MODE_INT, CDMA_ROAMING_MODE_RADIO_DEFAULT);
- // Used for Sim card State detection app
- sDefaults.putStringArray(KEY_SIM_PROVISIONING_STATUS_DETECTION_CARRIER_APP_STRING_ARRAY,
- null);
+ // Carrier Signalling Receivers
+ sDefaults.putStringArray(KEY_SIGNAL_REDIRECTION_RECEIVER_STRING_ARRAY, null);
+ sDefaults.putStringArray(KEY_SIGNAL_DCFAILURE_RECEIVER_STRING_ARRAY, null);
+ sDefaults.putStringArray(KEY_SIGNAL_PCO_RECEIVER_STRING_ARRAY, null);
+ sDefaults.putString(KEY_CARRIER_SETUP_APP_STRING, "");
+
+ // Rat families: {GPRS, EDGE}, {EVDO, EVDO_A, EVDO_B}, {UMTS, HSPA, HSDPA, HSUPA, HSPAP},
+ // {LTE, LTE_CA}
+ // Order is important - lowest precidence first
+ sDefaults.putStringArray(KEY_RATCHET_RAT_FAMILIES,
+ new String[]{"1,2","7,8,12","3,11,9,10,15","14,19"});
+ sDefaults.putBoolean(KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL, false);
+ sDefaults.putBoolean(KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL, false);
+ sDefaults.putBoolean(KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL, true);
+ sDefaults.putBoolean(KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL, true);
+ sDefaults.putBoolean(KEY_WIFI_CALLS_CAN_BE_HD_AUDIO, true);
+ sDefaults.putBoolean(KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO, true);
+
+ sDefaults.putStringArray(KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY, null);
+ sDefaults.putBoolean(KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL, false);
+ sDefaults.putBoolean(KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL, false);
+ sDefaults.putStringArray(FILTERED_CNAP_NAMES_STRING_ARRAY, null);
}
/**
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index 9eb1304..f5e422d 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -194,6 +194,38 @@
*/
public static final int VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED = 50;
+ /**
+ * The call was terminated because it was pulled to another device.
+ * {@hide}
+ */
+ public static final int CALL_PULLED = 51;
+
+ /**
+ * The call was terminated because it was answered on another device.
+ * {@hide}
+ */
+ public static final int ANSWERED_ELSEWHERE = 52;
+
+ /**
+ * The call was terminated because the maximum allowable number of calls has been reached.
+ * {@hide}
+ */
+ public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 53;
+
+ /**
+ * The call was terminated because cellular data has been disabled.
+ * Used when in a video call and the user disables cellular data via the settings.
+ * {@hide}
+ */
+ public static final int DATA_DISABLED = 54;
+
+ /**
+ * The call was terminated because the data policy has disabled cellular data.
+ * Used when in a video call and the user has exceeded the device data limit.
+ * {@hide}
+ */
+ public static final int DATA_LIMIT_REACHED = 55;
+
//*********************************************************************************************
// When adding a disconnect type:
// 1) Please assign the new type the next id value below.
@@ -202,14 +234,14 @@
// 4) Update toString() with the newly added disconnect type.
// 5) Update android.telecom.DisconnectCauseUtil with any mappings to a telecom.DisconnectCause.
//
- // NextId: 50
+ // NextId: 56
//*********************************************************************************************
/** Smallest valid value for call disconnect codes. */
public static final int MINIMUM_VALID_VALUE = NOT_DISCONNECTED;
/** Largest valid value for call disconnect codes. */
- public static final int MAXIMUM_VALID_VALUE = VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED;
+ public static final int MAXIMUM_VALID_VALUE = DATA_LIMIT_REACHED;
/** Private constructor to avoid class instantiation. */
private DisconnectCause() {
@@ -318,7 +350,17 @@
case CDMA_ALREADY_ACTIVATED:
return "CDMA_ALREADY_ACTIVATED";
case VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED:
- return "VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED";
+ return "VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED";
+ case CALL_PULLED:
+ return "CALL_PULLED";
+ case ANSWERED_ELSEWHERE:
+ return "ANSWERED_ELSEWHERE";
+ case MAXIMUM_NUMBER_OF_CALLS_REACHED:
+ return "MAXIMUM_NUMER_OF_CALLS_REACHED";
+ case DATA_DISABLED:
+ return "DATA_DISABLED";
+ case DATA_LIMIT_REACHED:
+ return "DATA_LIMIT_REACHED";
default:
return "INVALID: " + cause;
}
diff --git a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl b/telephony/java/android/telephony/PcoData.aidl
similarity index 82%
copy from core/java/com/android/internal/app/EphemeralResolveInfo.aidl
copy to telephony/java/android/telephony/PcoData.aidl
index 529527b..a93b8d3 100644
--- a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl
+++ b/telephony/java/android/telephony/PcoData.aidl
@@ -1,5 +1,6 @@
/*
-** Copyright 2015, The Android Open Source Project
+**
+** 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.
@@ -14,6 +15,7 @@
** limitations under the License.
*/
-package com.android.internal.app;
+package android.telephony;
-parcelable EphemeralResolveInfo;
+parcelable PcoData;
+
diff --git a/telephony/java/android/telephony/PcoData.java b/telephony/java/android/telephony/PcoData.java
new file mode 100644
index 0000000..3e735e7
--- /dev/null
+++ b/telephony/java/android/telephony/PcoData.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Contains Carrier-specific (and opaque) Protocol configuration Option
+ * Data. In general this is only passed on to carrier-specific applications
+ * for interpretation.
+ *
+ * @hide
+ */
+public class PcoData implements Parcelable {
+
+ public final int cid;
+ public final String bearerProto;
+ public final int pcoId;
+ public final byte[] contents;
+
+ public PcoData(int cid, String bearerProto, int pcoId, byte[]contents) {
+ this.cid = cid;
+ this.bearerProto = bearerProto;
+ this.pcoId = pcoId;
+ this.contents = contents;
+ }
+
+ public PcoData(Parcel in) {
+ cid = in.readInt();
+ bearerProto = in.readString();
+ pcoId = in.readInt();
+ contents = in.createByteArray();
+ }
+
+ /**
+ * {@link Parcelable#writeToParcel}
+ */
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(cid);
+ out.writeString(bearerProto);
+ out.writeInt(pcoId);
+ out.writeByteArray(contents);
+ }
+
+ /**
+ * {@link Parcelable#describeContents}
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@link Parcelable.Creator}
+ *
+ * @hide
+ */
+ public static final Parcelable.Creator<PcoData> CREATOR = new Parcelable.Creator() {
+ public PcoData createFromParcel(Parcel in) {
+ return new PcoData(in);
+ }
+
+ public PcoData[] newArray(int size) {
+ return new PcoData[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "PcoData(" + cid + ", " + bearerProto + ", " + pcoId + ", contents[" +
+ contents.length + "])";
+ }
+}
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 962a600..03d6d21 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -30,6 +30,7 @@
import android.os.SystemProperties;
import android.provider.Contacts;
import android.provider.ContactsContract;
+import android.telecom.PhoneAccount;
import android.text.Editable;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
@@ -2599,6 +2600,48 @@
}
/**
+ * Given a {@link Uri} with a {@code sip} scheme, attempts to build an equivalent {@code tel}
+ * scheme {@link Uri}. If the source {@link Uri} does not contain a valid number, or is not
+ * using the {@code sip} scheme, the original {@link Uri} is returned.
+ *
+ * @param source The {@link Uri} to convert.
+ * @return The equivalent {@code tel} scheme {@link Uri}.
+ *
+ * @hide
+ */
+ public static Uri convertSipUriToTelUri(Uri source) {
+ // A valid SIP uri has the format: sip:user:password@host:port;uri-parameters?headers
+ // Per RFC3261, the "user" can be a telephone number.
+ // For example: sip:1650555121;phone-context=blah.com@host.com
+ // In this case, the phone number is in the user field of the URI, and the parameters can be
+ // ignored.
+ //
+ // A SIP URI can also specify a phone number in a format similar to:
+ // sip:+1-212-555-1212@something.com;user=phone
+ // In this case, the phone number is again in user field and the parameters can be ignored.
+ // We can get the user field in these instances by splitting the string on the @, ;, or :
+ // and looking at the first found item.
+
+ String scheme = source.getScheme();
+
+ if (!PhoneAccount.SCHEME_SIP.equals(scheme)) {
+ // Not a sip URI, bail.
+ return source;
+ }
+
+ String number = source.getSchemeSpecificPart();
+ String numberParts[] = number.split("[@;:]");
+
+ if (numberParts.length == 0) {
+ // Number not found, bail.
+ return source;
+ }
+ number = numberParts[0];
+
+ return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
+ }
+
+ /**
* This function handles the plus code conversion
* If the number format is
* 1)+1NANP,remove +,
diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java
index 2bfaf1b..b530a64 100644
--- a/telephony/java/android/telephony/RadioAccessFamily.java
+++ b/telephony/java/android/telephony/RadioAccessFamily.java
@@ -47,6 +47,7 @@
public static final int RAF_HSPAP = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP);
public static final int RAF_GSM = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_GSM);
public static final int RAF_TD_SCDMA = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA);
+ public static final int RAF_LTE_CA = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA);
// Grouping of RAFs
private static final int GSM = RAF_GSM | RAF_GPRS | RAF_EDGE;
@@ -54,6 +55,7 @@
private static final int CDMA = RAF_IS95A | RAF_IS95B | RAF_1xRTT;
private static final int EVDO = RAF_EVDO_0 | RAF_EVDO_A | RAF_EVDO_B | RAF_EHRPD;
private static final int WCDMA = HS | RAF_UMTS;
+ private static final int LTE = RAF_LTE | RAF_LTE_CA;
/* Phone ID of phone */
private int mPhoneId;
@@ -162,19 +164,19 @@
raf = CDMA | EVDO;
break;
case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO:
- raf = RAF_LTE | CDMA | EVDO;
+ raf = LTE | CDMA | EVDO;
break;
case RILConstants.NETWORK_MODE_LTE_GSM_WCDMA:
- raf = RAF_LTE | GSM | WCDMA;
+ raf = LTE | GSM | WCDMA;
break;
case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA:
- raf = RAF_LTE | CDMA | EVDO | GSM | WCDMA;
+ raf = LTE | CDMA | EVDO | GSM | WCDMA;
break;
case RILConstants.NETWORK_MODE_LTE_ONLY:
- raf = RAF_LTE;
+ raf = LTE;
break;
case RILConstants.NETWORK_MODE_LTE_WCDMA:
- raf = RAF_LTE | WCDMA;
+ raf = LTE | WCDMA;
break;
case RILConstants.NETWORK_MODE_CDMA_NO_EVDO:
raf = CDMA;
@@ -192,28 +194,28 @@
raf = RAF_TD_SCDMA | WCDMA;
break;
case RILConstants.NETWORK_MODE_LTE_TDSCDMA:
- raf = RAF_LTE | RAF_TD_SCDMA;
+ raf = LTE | RAF_TD_SCDMA;
break;
case RILConstants.NETWORK_MODE_TDSCDMA_GSM:
raf = RAF_TD_SCDMA | GSM;
break;
case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM:
- raf = RAF_LTE | RAF_TD_SCDMA | GSM;
+ raf = LTE | RAF_TD_SCDMA | GSM;
break;
case RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA:
raf = RAF_TD_SCDMA | GSM | WCDMA;
break;
case RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA:
- raf = RAF_LTE | RAF_TD_SCDMA | WCDMA;
+ raf = LTE | RAF_TD_SCDMA | WCDMA;
break;
case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA:
- raf = RAF_LTE | RAF_TD_SCDMA | GSM | WCDMA;
+ raf = LTE | RAF_TD_SCDMA | GSM | WCDMA;
break;
case RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
raf = RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
break;
case RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
- raf = RAF_LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
+ raf = LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
break;
default:
raf = RAF_UNKNOWN;
@@ -232,6 +234,7 @@
raf = ((WCDMA & raf) > 0) ? (WCDMA | raf) : raf;
raf = ((CDMA & raf) > 0) ? (CDMA | raf) : raf;
raf = ((EVDO & raf) > 0) ? (EVDO | raf) : raf;
+ raf = ((LTE & raf) > 0) ? (LTE | raf) : raf;
return raf;
}
@@ -254,19 +257,19 @@
case (CDMA | EVDO):
type = RILConstants.NETWORK_MODE_CDMA;
break;
- case (RAF_LTE | CDMA | EVDO):
+ case (LTE | CDMA | EVDO):
type = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
break;
- case (RAF_LTE | GSM | WCDMA):
+ case (LTE | GSM | WCDMA):
type = RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
break;
- case (RAF_LTE | CDMA | EVDO | GSM | WCDMA):
+ case (LTE | CDMA | EVDO | GSM | WCDMA):
type = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
break;
- case RAF_LTE:
+ case LTE:
type = RILConstants.NETWORK_MODE_LTE_ONLY;
break;
- case (RAF_LTE | WCDMA):
+ case (LTE | WCDMA):
type = RILConstants.NETWORK_MODE_LTE_WCDMA;
break;
case CDMA:
@@ -284,28 +287,28 @@
case (RAF_TD_SCDMA | WCDMA):
type = RILConstants.NETWORK_MODE_TDSCDMA_WCDMA;
break;
- case (RAF_LTE | RAF_TD_SCDMA):
+ case (LTE | RAF_TD_SCDMA):
type = RILConstants.NETWORK_MODE_LTE_TDSCDMA;
break;
case (RAF_TD_SCDMA | GSM):
type = RILConstants.NETWORK_MODE_TDSCDMA_GSM;
break;
- case (RAF_LTE | RAF_TD_SCDMA | GSM):
+ case (LTE | RAF_TD_SCDMA | GSM):
type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM;
break;
case (RAF_TD_SCDMA | GSM | WCDMA):
type = RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA;
break;
- case (RAF_LTE | RAF_TD_SCDMA | WCDMA):
+ case (LTE | RAF_TD_SCDMA | WCDMA):
type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA;
break;
- case (RAF_LTE | RAF_TD_SCDMA | GSM | WCDMA):
+ case (LTE | RAF_TD_SCDMA | GSM | WCDMA):
type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA;
break;
case (RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA):
type = RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
break;
- case (RAF_LTE | RAF_TD_SCDMA | RAF_LTE | CDMA | EVDO | GSM | WCDMA):
+ case (LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA):
type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
break;
default:
@@ -339,6 +342,7 @@
case "CDMA": return CDMA;
case "EVDO": return EVDO;
case "WCDMA": return WCDMA;
+ case "LTE_CA": return RAF_LTE_CA;
default: return RAF_UNKNOWN;
}
}
diff --git a/telephony/java/android/telephony/Rlog.java b/telephony/java/android/telephony/Rlog.java
index 2a7f7af..b4f400f 100644
--- a/telephony/java/android/telephony/Rlog.java
+++ b/telephony/java/android/telephony/Rlog.java
@@ -85,5 +85,13 @@
return Log.isLoggable(tag, level);
}
+ /**
+ * Redact personally identifiable information for production users.
+ * If log tag is loggable in verbose mode, return the original string, otherwise return XXX.
+ */
+ public static String pii(String tag, Object pii) {
+ return (isLoggable(tag, Log.VERBOSE) ? String.valueOf(pii) : "XXX");
+ }
+
}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 39a9295..6151e5b 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -155,6 +155,12 @@
*/
public static final int RIL_RADIO_TECHNOLOGY_IWLAN = 18;
+ /**
+ * LTE_CA
+ * @hide
+ */
+ public static final int RIL_RADIO_TECHNOLOGY_LTE_CA = 19;
+
/** @hide */
public static final int RIL_RADIO_CDMA_TECHNOLOGY_BITMASK =
(1 << (RIL_RADIO_TECHNOLOGY_IS95A - 1))
@@ -234,6 +240,8 @@
private boolean mIsDataRoamingFromRegistration;
+ private boolean mIsUsingCarrierAggregation;
+
/**
* get String description of roaming type
* @hide
@@ -312,6 +320,7 @@
mCdmaEriIconMode = s.mCdmaEriIconMode;
mIsEmergencyOnly = s.mIsEmergencyOnly;
mIsDataRoamingFromRegistration = s.mIsDataRoamingFromRegistration;
+ mIsUsingCarrierAggregation = s.mIsUsingCarrierAggregation;
}
/**
@@ -340,6 +349,7 @@
mCdmaEriIconMode = in.readInt();
mIsEmergencyOnly = in.readInt() != 0;
mIsDataRoamingFromRegistration = in.readInt() != 0;
+ mIsUsingCarrierAggregation = in.readInt() != 0;
}
public void writeToParcel(Parcel out, int flags) {
@@ -365,6 +375,7 @@
out.writeInt(mCdmaEriIconMode);
out.writeInt(mIsEmergencyOnly ? 1 : 0);
out.writeInt(mIsDataRoamingFromRegistration ? 1 : 0);
+ out.writeInt(mIsUsingCarrierAggregation ? 1 : 0);
}
public int describeContents() {
@@ -674,7 +685,8 @@
&& equalsHandlesNulls(mCdmaDefaultRoamingIndicator,
s.mCdmaDefaultRoamingIndicator)
&& mIsEmergencyOnly == s.mIsEmergencyOnly
- && mIsDataRoamingFromRegistration == s.mIsDataRoamingFromRegistration);
+ && mIsDataRoamingFromRegistration == s.mIsDataRoamingFromRegistration
+ && mIsUsingCarrierAggregation == s.mIsUsingCarrierAggregation);
}
/**
@@ -746,6 +758,9 @@
case RIL_RADIO_TECHNOLOGY_TD_SCDMA:
rtString = "TD-SCDMA";
break;
+ case RIL_RADIO_TECHNOLOGY_LTE_CA:
+ rtString = "LTE_CA";
+ break;
default:
rtString = "Unexpected";
Rlog.w(LOG_TAG, "Unexpected radioTechnology=" + rt);
@@ -779,7 +794,8 @@
+ " RoamInd=" + mCdmaRoamingIndicator
+ " DefRoamInd=" + mCdmaDefaultRoamingIndicator
+ " EmergOnly=" + mIsEmergencyOnly
- + " IsDataRoamingFromRegistration=" + mIsDataRoamingFromRegistration);
+ + " IsDataRoamingFromRegistration=" + mIsDataRoamingFromRegistration
+ + " IsUsingCarrierAggregation=" + mIsUsingCarrierAggregation);
}
private void setNullState(int state) {
@@ -806,6 +822,7 @@
mCdmaEriIconMode = -1;
mIsEmergencyOnly = false;
mIsDataRoamingFromRegistration = false;
+ mIsUsingCarrierAggregation = false;
}
public void setStateOutOfService() {
@@ -979,6 +996,7 @@
mCdmaDefaultRoamingIndicator = m.getInt("cdmaDefaultRoamingIndicator");
mIsEmergencyOnly = m.getBoolean("emergencyOnly");
mIsDataRoamingFromRegistration = m.getBoolean("isDataRoamingFromRegistration");
+ mIsUsingCarrierAggregation = m.getBoolean("isUsingCarrierAggregation");
}
/**
@@ -1008,21 +1026,42 @@
m.putInt("cdmaDefaultRoamingIndicator", mCdmaDefaultRoamingIndicator);
m.putBoolean("emergencyOnly", Boolean.valueOf(mIsEmergencyOnly));
m.putBoolean("isDataRoamingFromRegistration", Boolean.valueOf(mIsDataRoamingFromRegistration));
+ m.putBoolean("isUsingCarrierAggregation", Boolean.valueOf(mIsUsingCarrierAggregation));
}
/** @hide */
public void setRilVoiceRadioTechnology(int rt) {
+ if (rt == RIL_RADIO_TECHNOLOGY_LTE_CA) {
+ rt = RIL_RADIO_TECHNOLOGY_LTE;
+ }
+
this.mRilVoiceRadioTechnology = rt;
}
/** @hide */
public void setRilDataRadioTechnology(int rt) {
+ if (rt == RIL_RADIO_TECHNOLOGY_LTE_CA) {
+ rt = RIL_RADIO_TECHNOLOGY_LTE;
+ this.mIsUsingCarrierAggregation = true;
+ } else {
+ this.mIsUsingCarrierAggregation = false;
+ }
this.mRilDataRadioTechnology = rt;
if (VDBG) Rlog.d(LOG_TAG, "[ServiceState] setRilDataRadioTechnology=" +
mRilDataRadioTechnology);
}
/** @hide */
+ public boolean isUsingCarrierAggregation() {
+ return mIsUsingCarrierAggregation;
+ }
+
+ /** @hide */
+ public void setIsUsingCarrierAggregation(boolean ca) {
+ mIsUsingCarrierAggregation = ca;
+ }
+
+ /** @hide */
public void setCssIndicator(int css) {
this.mCssIndicator = (css != 0);
}
@@ -1088,6 +1127,8 @@
return TelephonyManager.NETWORK_TYPE_TD_SCDMA;
case ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN:
return TelephonyManager.NETWORK_TYPE_IWLAN;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA:
+ return TelephonyManager.NETWORK_TYPE_LTE_CA;
default:
return TelephonyManager.NETWORK_TYPE_UNKNOWN;
}
@@ -1139,7 +1180,9 @@
|| radioTechnology == RIL_RADIO_TECHNOLOGY_HSPAP
|| radioTechnology == RIL_RADIO_TECHNOLOGY_GSM
|| radioTechnology == RIL_RADIO_TECHNOLOGY_TD_SCDMA
- || radioTechnology == RIL_RADIO_TECHNOLOGY_IWLAN;
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_IWLAN
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_LTE_CA;
+
}
/** @hide */
@@ -1154,6 +1197,12 @@
}
/** @hide */
+ public static boolean isLte(int radioTechnology) {
+ return radioTechnology == RIL_RADIO_TECHNOLOGY_LTE ||
+ radioTechnology == RIL_RADIO_TECHNOLOGY_LTE_CA;
+ }
+
+ /** @hide */
public static boolean bearerBitmapHasCdma(int radioTechnologyBitmap) {
return (RIL_RADIO_CDMA_TECHNOLOGY_BITMASK & radioTechnologyBitmap) != 0;
}
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index b5cf212e..6229ed9 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -90,14 +90,6 @@
private int mDataRoaming;
/**
- * Sim Provisioning Status:
- * {@See SubscriptionManager#SIM_PROVISIONED}
- * {@See SubscriptionManager#SIM_UNPROVISIONED_COLD}
- * {@See SubscriptionManager#SIM_UNPROVISIONED_OUT_OF_CREDIT}
- */
- private int mSimProvisioningStatus;
-
- /**
* SIM Icon bitmap
*/
private Bitmap mIconBitmap;
@@ -122,7 +114,7 @@
*/
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
- Bitmap icon, int mcc, int mnc, String countryIso, int simProvisioningStatus) {
+ Bitmap icon, int mcc, int mnc, String countryIso) {
this.mId = id;
this.mIccId = iccId;
this.mSimSlotIndex = simSlotIndex;
@@ -136,7 +128,6 @@
this.mMcc = mcc;
this.mMnc = mnc;
this.mCountryIso = countryIso;
- this.mSimProvisioningStatus = simProvisioningStatus;
}
/**
@@ -273,17 +264,6 @@
}
/**
- * @return Sim Provisioning Status
- * {@See SubscriptionManager#SIM_PROVISIONED}
- * {@See SubscriptionManager#SIM_UNPROVISIONED_COLD}
- * {@See SubscriptionManager#SIM_UNPROVISIONED_OUT_OF_CREDIT}
- * @hide
- */
- public int getSimProvisioningStatus() {
- return this.mSimProvisioningStatus;
- }
-
- /**
* @return the MCC.
*/
public int getMcc() {
@@ -319,12 +299,10 @@
int mcc = source.readInt();
int mnc = source.readInt();
String countryIso = source.readString();
- int simProvisioningStatus = source.readInt();
Bitmap iconBitmap = Bitmap.CREATOR.createFromParcel(source);
return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
- nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
- simProvisioningStatus);
+ nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso);
}
@Override
@@ -347,7 +325,6 @@
dest.writeInt(mMcc);
dest.writeInt(mMnc);
dest.writeString(mCountryIso);
- dest.writeInt(mSimProvisioningStatus);
mIconBitmap.writeToParcel(dest, flags);
}
@@ -378,6 +355,6 @@
+ " displayName=" + mDisplayName + " carrierName=" + mCarrierName
+ " nameSource=" + mNameSource + " iconTint=" + mIconTint
+ " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
- + " mnc " + mMnc + " SimProvisioningStatus " + mSimProvisioningStatus +"}";
+ + " mnc " + mMnc + "}";
}
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index c49966a..dd6f9cb 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -232,25 +232,12 @@
/** Indicates that data roaming is disabled for a subscription */
public static final int DATA_ROAMING_DISABLE = 0;
- /** Sim provisioning status: provisioned */
- /** @hide */
- public static final int SIM_PROVISIONED = 0;
-
- /** Sim provisioning status: un-provisioned due to cold sim */
- /** @hide */
- public static final int SIM_UNPROVISIONED_COLD = 1;
-
- /** Sim provisioning status: un-provisioned due to out of credit */
- /** @hide */
- public static final int SIM_UNPROVISIONED_OUT_OF_CREDIT = 2;
-
- /** Maximum possible sim provisioning status */
- /** @hide */
- public static final int MAX_SIM_PROVISIONING_STATUS = SIM_UNPROVISIONED_OUT_OF_CREDIT;
-
/** @hide */
public static final int DATA_ROAMING_DEFAULT = DATA_ROAMING_DISABLE;
+ /** @hide */
+ public static final int SIM_PROVISIONED = 0;
+
/**
* TelephonyProvider column name for the MCC associated with a SIM.
* <P>Type: INTEGER (int)</P>
@@ -843,40 +830,6 @@
}
/**
- * Set Sim Provisioning Status by subscription ID
- * @param simProvisioningStatus with the subscription
- * {@See SubscriptionManager#SIM_PROVISIONED}
- * {@See SubscriptionManager#SIM_UNPROVISIONED_COLD}
- * {@See SubscriptionManager#SIM_UNPROVISIONED_OUT_OF_CREDIT}
- * @param subId the unique SubInfoRecord index in database
- * @return the number of records updated
- * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
- * @hide
- */
- public int setSimProvisioningStatus(int simProvisioningStatus, int subId) {
- if (VDBG) {
- logd("[setSimProvisioningStatus]+ status:" + simProvisioningStatus + " subId:" + subId);
- }
- if (simProvisioningStatus < 0 || simProvisioningStatus > MAX_SIM_PROVISIONING_STATUS ||
- !isValidSubscriptionId(subId)) {
- logd("[setSimProvisioningStatus]- fail");
- return -1;
- }
-
- int result = 0;
-
- try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
- if (iSub != null) {
- result = iSub.setSimProvisioningStatus(simProvisioningStatus, subId);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return result;
- }
-
- /**
* Get slotId associated with the subscription.
* @return slotId as a positive integer or a negative value if an error either
* SIM_NOT_INSERTED or < 0 if an invalid slot index
diff --git a/telecomm/java/android/telecom/ParcelableCallAnalytics.aidl b/telephony/java/android/telephony/TelephonyHistogram.aidl
similarity index 74%
copy from telecomm/java/android/telecom/ParcelableCallAnalytics.aidl
copy to telephony/java/android/telephony/TelephonyHistogram.aidl
index b7e78d1..8de81cf 100644
--- a/telecomm/java/android/telecom/ParcelableCallAnalytics.aidl
+++ b/telephony/java/android/telephony/TelephonyHistogram.aidl
@@ -1,11 +1,11 @@
/*
- * Copyright 2016, The Android Open Source Project
+ * 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
+ * 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,
@@ -13,10 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package android.telecom;
-
+package android.telephony;
/**
- * {@hide}
+ * @hide
*/
-parcelable ParcelableCallAnalytics;
+parcelable TelephonyHistogram;
diff --git a/telephony/java/android/telephony/TelephonyHistogram.java b/telephony/java/android/telephony/TelephonyHistogram.java
new file mode 100644
index 0000000..e1c3d7b
--- /dev/null
+++ b/telephony/java/android/telephony/TelephonyHistogram.java
@@ -0,0 +1,313 @@
+/*
+ * 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.telephony;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Parcelable class to store Telephony histogram.
+ * @hide
+ */
+@SystemApi
+public final class TelephonyHistogram implements Parcelable {
+ // Type of Telephony histogram Eg: RIL histogram will have all timing data associated with
+ // RIL calls. Similarly we can have any other Telephony histogram.
+ private final int mCategory;
+
+ // Unique Id identifying a sample within particular category of histogram
+ private final int mId;
+
+ // Min time taken in ms
+ private int mMinTimeMs;
+
+ // Max time taken in ms
+ private int mMaxTimeMs;
+
+ // Average time taken in ms
+ private int mAverageTimeMs;
+
+ // Total count of samples
+ private int mSampleCount;
+
+ // Array storing time taken for first #RANGE_CALCULATION_COUNT samples of histogram.
+ private int[] mInitialTimings;
+
+ // Total number of time ranges expected (must be greater than 1)
+ private final int mBucketCount;
+
+ // Array storing endpoints of range buckets. Calculated based on values of minTime & maxTime
+ // after totalTimeCount is #RANGE_CALCULATION_COUNT.
+ private final int[] mBucketEndPoints;
+
+ // Array storing counts for each time range starting from smallest value range
+ private final int[] mBucketCounters;
+
+ /**
+ * Constant for Telephony category
+ */
+ public static final int TELEPHONY_CATEGORY_RIL = 1;
+
+ // Count of Histogram samples after which time buckets are created.
+ private static final int RANGE_CALCULATION_COUNT = 10;
+
+
+ // Constant used to indicate #initialTimings is null while parceling
+ private static final int ABSENT = 0;
+
+ // Constant used to indicate #initialTimings is not null while parceling
+ private static final int PRESENT = 1;
+
+ // Throws exception if #totalBuckets is not greater than one.
+ public TelephonyHistogram (int category, int id, int bucketCount) {
+ if (bucketCount <= 1) {
+ throw new IllegalArgumentException("Invalid number of buckets");
+ }
+ mCategory = category;
+ mId = id;
+ mMinTimeMs = Integer.MAX_VALUE;
+ mMaxTimeMs = 0;
+ mAverageTimeMs = 0;
+ mSampleCount = 0;
+ mInitialTimings = new int[RANGE_CALCULATION_COUNT];
+ mBucketCount = bucketCount;
+ mBucketEndPoints = new int[bucketCount - 1];
+ mBucketCounters = new int[bucketCount];
+ }
+
+ public TelephonyHistogram(TelephonyHistogram th) {
+ mCategory = th.getCategory();
+ mId = th.getId();
+ mMinTimeMs = th.getMinTime();
+ mMaxTimeMs = th.getMaxTime();
+ mAverageTimeMs = th.getAverageTime();
+ mSampleCount = th.getSampleCount();
+ mInitialTimings = th.getInitialTimings();
+ mBucketCount = th.getBucketCount();
+ mBucketEndPoints = th.getBucketEndPoints();
+ mBucketCounters = th.getBucketCounters();
+ }
+
+ public int getCategory() {
+ return mCategory;
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public int getMinTime() {
+ return mMinTimeMs;
+ }
+
+ public int getMaxTime() {
+ return mMaxTimeMs;
+ }
+
+ public int getAverageTime() {
+ return mAverageTimeMs;
+ }
+
+ public int getSampleCount () {
+ return mSampleCount;
+ }
+
+ private int[] getInitialTimings() {
+ return mInitialTimings;
+ }
+
+ public int getBucketCount() {
+ return mBucketCount;
+ }
+
+ public int[] getBucketEndPoints() {
+ if (mSampleCount > 1 && mSampleCount < 10) {
+ int[] tempEndPoints = new int[mBucketCount - 1];
+ calculateBucketEndPoints(tempEndPoints);
+ return tempEndPoints;
+ } else {
+ return getDeepCopyOfArray(mBucketEndPoints);
+ }
+ }
+
+ public int[] getBucketCounters() {
+ if (mSampleCount > 1 && mSampleCount < 10) {
+ int[] tempEndPoints = new int[mBucketCount - 1];
+ int[] tempBucketCounters = new int[mBucketCount];
+ calculateBucketEndPoints(tempEndPoints);
+ for (int j = 0; j < mSampleCount; j++) {
+ addToBucketCounter(tempEndPoints, tempBucketCounters, mInitialTimings[j]);
+ }
+ return tempBucketCounters;
+ } else {
+ return getDeepCopyOfArray(mBucketCounters);
+ }
+ }
+
+ private int[] getDeepCopyOfArray(int[] array) {
+ int[] clone = new int[array.length];
+ System.arraycopy(array, 0, clone, 0, array.length);
+ return clone;
+ }
+
+ private void addToBucketCounter(int[] bucketEndPoints, int[] bucketCounters, int time) {
+ int i;
+ for (i = 0; i < bucketEndPoints.length; i++) {
+ if (time <= bucketEndPoints[i]) {
+ bucketCounters[i]++;
+ return;
+ }
+ }
+ bucketCounters[i]++;
+ }
+
+ private void calculateBucketEndPoints(int[] bucketEndPoints) {
+ for (int i = 1; i < mBucketCount; i++) {
+ int endPt = mMinTimeMs + (i * (mMaxTimeMs - mMinTimeMs)) / mBucketCount;
+ bucketEndPoints[i - 1] = endPt;
+ }
+ }
+
+ // Add new value of time taken
+ // This function updates minTime, maxTime, averageTime & totalTimeCount every time it is
+ // called. initialTimings[] is updated if totalTimeCount <= #RANGE_CALCULATION_COUNT. When
+ // totalTimeCount = RANGE_CALCULATION_COUNT, based on the min, max time & the number of buckets
+ // expected, bucketEndPoints[] would be calculated. Then bucketCounters[] would be filled up
+ // using values stored in initialTimings[]. Thereafter bucketCounters[] will always be updated.
+ public void addTimeTaken(int time) {
+ // Initialize all fields if its first entry or if integer overflow is going to occur while
+ // trying to calculate averageTime
+ if (mSampleCount == 0 || (mSampleCount == Integer.MAX_VALUE)) {
+ if (mSampleCount == 0) {
+ mMinTimeMs = time;
+ mMaxTimeMs = time;
+ mAverageTimeMs = time;
+ } else {
+ mInitialTimings = new int[RANGE_CALCULATION_COUNT];
+ }
+ mSampleCount = 1;
+ Arrays.fill(mInitialTimings, 0);
+ mInitialTimings[0] = time;
+ Arrays.fill(mBucketEndPoints, 0);
+ Arrays.fill(mBucketCounters, 0);
+ } else {
+ if (time < mMinTimeMs) {
+ mMinTimeMs = time;
+ }
+ if (time > mMaxTimeMs) {
+ mMaxTimeMs = time;
+ }
+ long totalTime = ((long)mAverageTimeMs) * mSampleCount + time;
+ mAverageTimeMs = (int)(totalTime/++mSampleCount);
+
+ if (mSampleCount < RANGE_CALCULATION_COUNT) {
+ mInitialTimings[mSampleCount - 1] = time;
+ } else if (mSampleCount == RANGE_CALCULATION_COUNT) {
+ mInitialTimings[mSampleCount - 1] = time;
+
+ // Calculate bucket endpoints based on bucketCount expected
+ calculateBucketEndPoints(mBucketEndPoints);
+
+ // Use values stored in initialTimings[] to update bucketCounters
+ for (int j = 0; j < RANGE_CALCULATION_COUNT; j++) {
+ addToBucketCounter(mBucketEndPoints, mBucketCounters, mInitialTimings[j]);
+ }
+ mInitialTimings = null;
+ } else {
+ addToBucketCounter(mBucketEndPoints, mBucketCounters, time);
+ }
+
+ }
+ }
+
+ public String toString() {
+ String basic = " Histogram id = " + mId + " Time(ms): min = " + mMinTimeMs + " max = "
+ + mMaxTimeMs + " avg = " + mAverageTimeMs + " Count = " + mSampleCount;
+ if (mSampleCount < RANGE_CALCULATION_COUNT) {
+ return basic;
+ } else {
+ StringBuffer intervals = new StringBuffer(" Interval Endpoints:");
+ for (int i = 0; i < mBucketEndPoints.length; i++) {
+ intervals.append(" " + mBucketEndPoints[i]);
+ }
+ intervals.append(" Interval counters:");
+ for (int i = 0; i < mBucketCounters.length; i++) {
+ intervals.append(" " + mBucketCounters[i]);
+ }
+ return basic + intervals;
+ }
+ }
+
+ public static final Parcelable.Creator<TelephonyHistogram> CREATOR =
+ new Parcelable.Creator<TelephonyHistogram> () {
+
+ @Override
+ public TelephonyHistogram createFromParcel(Parcel in) {
+ return new TelephonyHistogram(in);
+ }
+
+ @Override
+ public TelephonyHistogram[] newArray(int size) {
+ return new TelephonyHistogram[size];
+ }
+ };
+
+ public TelephonyHistogram(Parcel in) {
+ mCategory = in.readInt();
+ mId = in.readInt();
+ mMinTimeMs = in.readInt();
+ mMaxTimeMs = in.readInt();
+ mAverageTimeMs = in.readInt();
+ mSampleCount = in.readInt();
+ if (in.readInt() == PRESENT) {
+ mInitialTimings = new int[RANGE_CALCULATION_COUNT];
+ in.readIntArray(mInitialTimings);
+ }
+ mBucketCount = in.readInt();
+ mBucketEndPoints = new int[mBucketCount - 1];
+ in.readIntArray(mBucketEndPoints);
+ mBucketCounters = new int[mBucketCount];
+ in.readIntArray(mBucketCounters);
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mCategory);
+ out.writeInt(mId);
+ out.writeInt(mMinTimeMs);
+ out.writeInt(mMaxTimeMs);
+ out.writeInt(mAverageTimeMs);
+ out.writeInt(mSampleCount);
+ if (mInitialTimings == null) {
+ out.writeInt(ABSENT);
+ } else {
+ out.writeInt(PRESENT);
+ out.writeIntArray(mInitialTimings);
+ }
+ out.writeInt(mBucketCount);
+ out.writeIntArray(mBucketEndPoints);
+ out.writeIntArray(mBucketCounters);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 2304346..6e504d1 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -34,8 +34,10 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.service.carrier.CarrierIdentifier;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
+import android.telephony.TelephonyHistogram;
import android.util.Log;
import com.android.internal.telecom.ITelecomService;
@@ -50,6 +52,7 @@
import java.io.FileInputStream;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
@@ -702,6 +705,50 @@
"android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
/**
+ * {@link android.telecom.Connection} event used to indicate that an IMS call has be
+ * successfully handed over from WIFI to LTE.
+ * <p>
+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+ * The {@link Bundle} parameter is expected to be null when this connection event is used.
+ * @hide
+ */
+ public static final String EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE =
+ "android.telephony.event.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE";
+
+ /**
+ * {@link android.telecom.Connection} event used to indicate that an IMS call failed to be
+ * handed over from LTE to WIFI.
+ * <p>
+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+ * The {@link Bundle} parameter is expected to be null when this connection event is used.
+ * @hide
+ */
+ public static final String EVENT_HANDOVER_TO_WIFI_FAILED =
+ "android.telephony.event.EVENT_HANDOVER_TO_WIFI_FAILED";
+
+ /**
+ * {@link android.telecom.Connection} event used to indicate that a video call was downgraded to
+ * audio because the data limit was reached.
+ * <p>
+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+ * The {@link Bundle} parameter is expected to be null when this connection event is used.
+ * @hide
+ */
+ public static final String EVENT_DOWNGRADE_DATA_LIMIT_REACHED =
+ "android.telephony.event.EVENT_DOWNGRADE_DATA_LIMIT_REACHED";
+
+ /**
+ * {@link android.telecom.Connection} event used to indicate that a video call was downgraded to
+ * audio because the data was disabled.
+ * <p>
+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+ * The {@link Bundle} parameter is expected to be null when this connection event is used.
+ * @hide
+ */
+ public static final String EVENT_DOWNGRADE_DATA_DISABLED =
+ "android.telephony.event.EVENT_DOWNGRADE_DATA_DISABLED";
+
+ /**
* Response codes for sim activation. Activation completed successfully.
* @hide
*/
@@ -1442,13 +1489,14 @@
public static final int NETWORK_TYPE_EHRPD = 14;
/** Current network is HSPA+ */
public static final int NETWORK_TYPE_HSPAP = 15;
- /** Current network is GSM {@hide} */
+ /** Current network is GSM */
public static final int NETWORK_TYPE_GSM = 16;
- /** Current network is TD_SCDMA {@hide} */
+ /** Current network is TD_SCDMA */
public static final int NETWORK_TYPE_TD_SCDMA = 17;
- /** Current network is IWLAN {@hide} */
+ /** Current network is IWLAN */
public static final int NETWORK_TYPE_IWLAN = 18;
-
+ /** Current network is LTE_CA {@hide} */
+ public static final int NETWORK_TYPE_LTE_CA = 19;
/**
* @return the NETWORK_TYPE_xxxx for current data connection.
*/
@@ -1651,6 +1699,7 @@
return NETWORK_CLASS_3_G;
case NETWORK_TYPE_LTE:
case NETWORK_TYPE_IWLAN:
+ case NETWORK_TYPE_LTE_CA:
return NETWORK_CLASS_4_G;
default:
return NETWORK_CLASS_UNKNOWN;
@@ -1714,6 +1763,8 @@
return "TD_SCDMA";
case NETWORK_TYPE_IWLAN:
return "IWLAN";
+ case NETWORK_TYPE_LTE_CA:
+ return "LTE_CA";
default:
return "UNKNOWN";
}
@@ -1756,6 +1807,11 @@
*@hide
*/
public static final int SIM_STATE_CARD_IO_ERROR = 8;
+ /** SIM card state: SIM Card restricted, present but not usable due to
+ * carrier restrictions.
+ *@hide
+ */
+ public static final int SIM_STATE_CARD_RESTRICTED = 9;
/**
* @return true if a ICC card is present
@@ -1909,7 +1965,7 @@
return getSimOperatorNumericForPhone(phoneId);
}
- /**
+ /**
* Returns the MCC+MNC (mobile country code + mobile network code) of the
* provider of the SIM for a particular subscription. 5 or 6 decimal digits.
* <p>
@@ -2461,6 +2517,152 @@
}
/**
+ * Enables or disables the visual voicemail client for a phone account.
+ *
+ * <p>Requires that the calling app is the default dialer, or has carrier privileges, or
+ * has permission {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+ * @see #hasCarrierPrivileges
+ *
+ * @param phoneAccountHandle the phone account to change the client state
+ * @param enabled the new state of the client
+ * @hide
+ */
+ @SystemApi
+ public void setVisualVoicemailEnabled(PhoneAccountHandle phoneAccountHandle, boolean enabled){
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.setVisualVoicemailEnabled(mContext.getOpPackageName(), phoneAccountHandle,
+ enabled);
+ }
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ }
+ }
+
+ /**
+ * Returns whether the visual voicemail client is enabled.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ *
+ * @param phoneAccountHandle the phone account to check for.
+ * @return {@code true} when the visual voicemail client is enabled for this client
+ * @hide
+ */
+ @SystemApi
+ public boolean isVisualVoicemailEnabled(PhoneAccountHandle phoneAccountHandle){
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.isVisualVoicemailEnabled(
+ mContext.getOpPackageName(), phoneAccountHandle);
+ }
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ }
+ return false;
+ }
+
+ /**
+ * Enables the visual voicemail SMS filter for a phone account. When the filter is
+ * enabled, Incoming SMS messages matching the OMTP VVM SMS interface will be redirected to the
+ * visual voicemail client with
+ * {@link android.provider.VoicemailContract.ACTION_VOICEMAIL_SMS_RECEIVED}.
+ *
+ * <p>This takes effect only when the caller is the default dialer. The enabled status and
+ * settings persist through default dialer changes, but the filter will only honor the setting
+ * set by the current default dialer.
+ *
+ *
+ * @param subId The subscription id of the phone account.
+ * @param settings The settings for the filter.
+ */
+ /** @hide */
+ public void enableVisualVoicemailSmsFilter(int subId,
+ VisualVoicemailSmsFilterSettings settings) {
+ if(settings == null){
+ throw new IllegalArgumentException("Settings cannot be null");
+ }
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.enableVisualVoicemailSmsFilter(mContext.getOpPackageName(), subId,
+ settings);
+ }
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ }
+
+ /**
+ * Disables the visual voicemail SMS filter for a phone account.
+ *
+ * <p>This takes effect only when the caller is the default dialer. The enabled status and
+ * settings persist through default dialer changes, but the filter will only honor the setting
+ * set by the current default dialer.
+ */
+ /** @hide */
+ public void disableVisualVoicemailSmsFilter(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.disableVisualVoicemailSmsFilter(mContext.getOpPackageName(), subId);
+ }
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ }
+
+ /**
+ * @returns the settings of the visual voicemail SMS filter for a phone account, or {@code null}
+ * if the filter is disabled.
+ *
+ * <p>This takes effect only when the caller is the default dialer. The enabled status and
+ * settings persist through default dialer changes, but the filter will only honor the setting
+ * set by the current default dialer.
+ */
+ /** @hide */
+ @Nullable
+ public VisualVoicemailSmsFilterSettings getVisualVoicemailSmsFilterSettings(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony
+ .getVisualVoicemailSmsFilterSettings(mContext.getOpPackageName(), subId);
+ }
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+
+ return null;
+ }
+
+ /**
+ * @returns the settings of the visual voicemail SMS filter for a phone account set by the
+ * package, or {@code null} if the filter is disabled.
+ *
+ * <p>Requires the calling app to have READ_PRIVILEGED_PHONE_STATE permission.
+ */
+ /** @hide */
+ @Nullable
+ public VisualVoicemailSmsFilterSettings getVisualVoicemailSmsFilterSettings(String packageName,
+ int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getSystemVisualVoicemailSmsFilterSettings(packageName, subId);
+ }
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+
+ return null;
+ }
+
+ /**
* Returns the voice mail count. Return 0 if unavailable, -1 if there are unread voice messages
* but the count is unknown.
* <p>
@@ -5246,4 +5448,239 @@
}
return false;
}
+
+ /**
+ * Return the application ID for the app type like {@link APPTYPE_CSIM}.
+ *
+ * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+ *
+ * @param appType the uicc app type like {@link APPTYPE_CSIM}
+ * @return Application ID for specificied app type or null if no uicc or error.
+ * @hide
+ */
+ public String getAidForAppType(int appType) {
+ return getAidForAppType(getDefaultSubscription(), appType);
+ }
+
+ /**
+ * Return the application ID for the app type like {@link APPTYPE_CSIM}.
+ *
+ * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+ *
+ * @param subId the subscription ID that this request applies to.
+ * @param appType the uicc app type, like {@link APPTYPE_CSIM}
+ * @return Application ID for specificied app type or null if no uicc or error.
+ * @hide
+ */
+ public String getAidForAppType(int subId, int appType) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getAidForAppType(subId, appType);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getAidForAppType", e);
+ }
+ return null;
+ }
+
+ /**
+ * Return the Electronic Serial Number.
+ *
+ * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+ *
+ * @return ESN or null if error.
+ * @hide
+ */
+ public String getEsn() {
+ return getEsn(getDefaultSubscription());
+ }
+
+ /**
+ * Return the Electronic Serial Number.
+ *
+ * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+ *
+ * @param subId the subscription ID that this request applies to.
+ * @return ESN or null if error.
+ * @hide
+ */
+ public String getEsn(int subId) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getEsn(subId);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getEsn", e);
+ }
+ return null;
+ }
+
+ /**
+ * Return the Preferred Roaming List Version
+ *
+ * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+ *
+ * @return PRLVersion or null if error.
+ * @hide
+ */
+ public String getCdmaPrlVersion() {
+ return getCdmaPrlVersion(getDefaultSubscription());
+ }
+
+ /**
+ * Return the Preferred Roaming List Version
+ *
+ * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+ *
+ * @param subId the subscription ID that this request applies to.
+ * @return PRLVersion or null if error.
+ * @hide
+ */
+ public String getCdmaPrlVersion(int subId) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getCdmaPrlVersion(subId);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getCdmaPrlVersion", e);
+ }
+ return null;
+ }
+
+ /**
+ * Get snapshot of Telephony histograms
+ * @return List of Telephony histograms
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges.
+ * @hide
+ */
+ @SystemApi
+ public List<TelephonyHistogram> getTelephonyHistograms() {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getTelephonyHistograms();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getTelephonyHistograms", e);
+ }
+ return null;
+ }
+
+ /**
+ * Set the allowed carrier list for slotId
+ * Require system privileges. In the future we may add this to carrier APIs.
+ *
+ * @return The number of carriers set successfully. Should be length of
+ * carrierList on success; -1 on error.
+ * @hide
+ */
+ public int setAllowedCarriers(int slotId, List<CarrierIdentifier> carriers) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.setAllowedCarriers(slotId, carriers);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#setAllowedCarriers", e);
+ }
+ return -1;
+ }
+
+ /**
+ * Get the allowed carrier list for slotId.
+ * Require system privileges. In the future we may add this to carrier APIs.
+ *
+ * @return List of {@link android.telephony.CarrierIdentifier}; empty list
+ * means all carriers are allowed.
+ * @hide
+ */
+ public List<CarrierIdentifier> getAllowedCarriers(int slotId) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getAllowedCarriers(slotId);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getAllowedCarriers", e);
+ }
+ return new ArrayList<CarrierIdentifier>(0);
+ }
+
+ /**
+ * Action set from carrier signalling broadcast receivers to enable/disable metered apns
+ * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+ * @param subId the subscription ID that this action applies to.
+ * @param enabled control enable or disable metered apns.
+ * @hide
+ */
+ public void carrierActionSetMeteredApnsEnabled(int subId, boolean enabled) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ service.carrierActionSetMeteredApnsEnabled(subId, enabled);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#carrierActionSetMeteredApnsEnabled", e);
+ }
+ }
+
+ /**
+ * Action set from carrier signalling broadcast receivers to enable/disable radio
+ * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+ * @param subId the subscription ID that this action applies to.
+ * @param enabled control enable or disable radio.
+ * @hide
+ */
+ public void carrierActionSetRadioEnabled(int subId, boolean enabled) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ service.carrierActionSetRadioEnabled(subId, enabled);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#carrierActionSetRadioEnabled", e);
+ }
+ }
+
+ /**
+ * Get aggregated video call data usage since boot.
+ * Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required.
+ * @return total data usage in bytes
+ * @hide
+ */
+ public long getVtDataUsage() {
+
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getVtDataUsage();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getVtDataUsage", e);
+ }
+ return 0;
+ }
+
+ /**
+ * Policy control of data connection. Usually used when data limit is passed.
+ * @param enabled True if enabling the data, otherwise disabling.
+ * @param subId sub id
+ * @hide
+ */
+ public void setPolicyDataEnabled(boolean enabled, int subId) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ service.setPolicyDataEnabled(enabled, subId);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#setPolicyDataEnabled", e);
+ }
+ }
}
+
diff --git a/telephony/java/android/telephony/VisualVoicemailSmsFilterSettings.aidl b/telephony/java/android/telephony/VisualVoicemailSmsFilterSettings.aidl
new file mode 100644
index 0000000..4b0539d
--- /dev/null
+++ b/telephony/java/android/telephony/VisualVoicemailSmsFilterSettings.aidl
@@ -0,0 +1,19 @@
+/*
+* 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.telephony;
+
+parcelable VisualVoicemailSmsFilterSettings;
diff --git a/telephony/java/android/telephony/VisualVoicemailSmsFilterSettings.java b/telephony/java/android/telephony/VisualVoicemailSmsFilterSettings.java
new file mode 100644
index 0000000..5b81027
--- /dev/null
+++ b/telephony/java/android/telephony/VisualVoicemailSmsFilterSettings.java
@@ -0,0 +1,174 @@
+/*
+ * 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.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Class to represent various settings for the visual voicemail SMS filter. When the filter is
+ * enabled, incoming SMS matching the generalized OMTP format:
+ *
+ * <p>[clientPrefix]:[prefix]:([key]=[value];)*
+ *
+ * <p>will be regarded as a visual voicemail SMS, and removed before reaching the SMS provider. The
+ * intent {@link android.provider.VoicemailContract#ACTION_VOICEMAIL_SMS_RECEIVED} will then be sent
+ * to the default dialer with the information extracted from the SMS.
+ *
+ * <p>Use {@link android.telephony.VisualVoicemailSmsFilterSettings.Builder} to construct this
+ * class.
+ *
+ * @see android.telephony.TelephonyManager#enableVisualVoicemailSmsFilter
+ *
+ * @hide
+ */
+public class VisualVoicemailSmsFilterSettings implements Parcelable {
+
+
+ /**
+ * The visual voicemail SMS message does not have to be a data SMS, and can be directed to any
+ * port.
+ *
+ * @hide
+ */
+ public static final int DESTINATION_PORT_ANY = -1;
+
+ /**
+ * The visual voicemail SMS message can be directed to any port, but must be a data SMS.
+ *
+ * @hide
+ */
+ public static final int DESTINATION_PORT_DATA_SMS = -2;
+
+ public static final String DEFAULT_CLIENT_PREFIX = "//VVM";
+ public static final List<String> DEFAULT_ORIGINATING_NUMBERS = Collections.emptyList();
+ public static final int DEFAULT_DESTINATION_PORT = DESTINATION_PORT_ANY;
+
+ /**
+ * Builder class for {@link VisualVoicemailSmsFilterSettings} objects.
+ *
+ * @hide
+ */
+ public static class Builder {
+
+ private String mClientPrefix = DEFAULT_CLIENT_PREFIX;
+ private List<String> mOriginatingNumbers = DEFAULT_ORIGINATING_NUMBERS;
+ private int mDestinationPort = DEFAULT_DESTINATION_PORT;
+
+ public VisualVoicemailSmsFilterSettings build() {
+ return new VisualVoicemailSmsFilterSettings(this);
+ }
+
+ /**
+ * Sets the client prefix for the visual voicemail SMS filter. The client prefix will appear
+ * at the start of a visual voicemail SMS message, followed by a colon(:).
+ */
+ public Builder setClientPrefix(String clientPrefix) {
+ if (clientPrefix == null) {
+ throw new IllegalArgumentException("Client prefix cannot be null");
+ }
+ mClientPrefix = clientPrefix;
+ return this;
+ }
+
+ /**
+ * Sets the originating number whitelist for the visual voicemail SMS filter. If the list is
+ * not null only the SMS messages from a number in the list can be considered as a visual
+ * voicemail SMS. Otherwise, messages from any address will be considered.
+ */
+ public Builder setOriginatingNumbers(List<String> originatingNumbers) {
+ if (originatingNumbers == null) {
+ throw new IllegalArgumentException("Originating numbers cannot be null");
+ }
+ mOriginatingNumbers = originatingNumbers;
+ return this;
+ }
+
+ /**
+ * Sets the destination port for the visual voicemail SMS filter.
+ *
+ * @param destinationPort The destination port, or {@link #DESTINATION_PORT_ANY}, or {@link
+ * #DESTINATION_PORT_DATA_SMS}
+ */
+ public Builder setDestinationPort(int destinationPort) {
+ mDestinationPort = destinationPort;
+ return this;
+ }
+
+ }
+
+ /**
+ * The client prefix for the visual voicemail SMS filter. The client prefix will appear at the
+ * start of a visual voicemail SMS message, followed by a colon(:).
+ */
+ public final String clientPrefix;
+
+ /**
+ * The originating number whitelist for the visual voicemail SMS filter of a phone account. If
+ * the list is not null only the SMS messages from a number in the list can be considered as a
+ * visual voicemail SMS. Otherwise, messages from any address will be considered.
+ */
+ public final List<String> originatingNumbers;
+
+ /**
+ * The destination port for the visual voicemail SMS filter, or {@link #DESTINATION_PORT_ANY},
+ * or {@link #DESTINATION_PORT_DATA_SMS}
+ */
+ public final int destinationPort;
+
+ /**
+ * Use {@link Builder} to construct
+ */
+ private VisualVoicemailSmsFilterSettings(Builder builder) {
+ clientPrefix = builder.mClientPrefix;
+ originatingNumbers = builder.mOriginatingNumbers;
+ destinationPort = builder.mDestinationPort;
+ }
+
+ public static final Creator<VisualVoicemailSmsFilterSettings> CREATOR =
+ new Creator<VisualVoicemailSmsFilterSettings>() {
+ @Override
+ public VisualVoicemailSmsFilterSettings createFromParcel(Parcel in) {
+ Builder builder = new Builder();
+ builder.setClientPrefix(in.readString());
+ builder.setOriginatingNumbers(in.createStringArrayList());
+ builder.setDestinationPort(in.readInt());
+
+ return builder.build();
+ }
+
+ @Override
+ public VisualVoicemailSmsFilterSettings[] newArray(int size) {
+ return new VisualVoicemailSmsFilterSettings[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(clientPrefix);
+ dest.writeStringList(originatingNumbers);
+ dest.writeInt(destinationPort);
+ }
+
+}
diff --git a/telephony/java/com/android/ims/ImsCallProfile.java b/telephony/java/com/android/ims/ImsCallProfile.java
index 92b70e8..36abfc9 100644
--- a/telephony/java/com/android/ims/ImsCallProfile.java
+++ b/telephony/java/com/android/ims/ImsCallProfile.java
@@ -205,6 +205,14 @@
*/
public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
+ /**
+ * Similar to {@link #EXTRA_CALL_RAT_TYPE}, except with a lowercase 'c'. Used to ensure
+ * compatibility with modems that are non-compliant with the {@link #EXTRA_CALL_RAT_TYPE}
+ * extra key. Should be removed when the non-compliant modems are fixed.
+ * @hide
+ */
+ public static final String EXTRA_CALL_RAT_TYPE_ALT = "callRadioTech";
+
public int mServiceType;
public int mCallType;
public int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;
@@ -448,6 +456,15 @@
}
/**
+ * Determines if the {@link ImsCallProfile} represents a video call.
+ *
+ * @return {@code true} if the profile is for a video call, {@code false} otherwise.
+ */
+ public boolean isVideoCall() {
+ return VideoProfile.isVideo(getVideoStateFromCallType(mCallType));
+ }
+
+ /**
* Determines if a video state is set in a video state bit-mask.
*
* @param videoState The video state bit mask.
diff --git a/telephony/java/com/android/ims/ImsExternalCallState.java b/telephony/java/com/android/ims/ImsExternalCallState.java
index 71c1837..da26073 100644
--- a/telephony/java/com/android/ims/ImsExternalCallState.java
+++ b/telephony/java/com/android/ims/ImsExternalCallState.java
@@ -19,6 +19,7 @@
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telecom.Log;
import android.telephony.Rlog;
/*
@@ -130,7 +131,7 @@
@Override
public String toString() {
return "ImsExternalCallState { mCallId = " + mCallId +
- ", mAddress = " + mAddress +
+ ", mAddress = " + Log.pii(mAddress) +
", mIsPullable = " + mIsPullable +
", mCallState = " + mCallState +
", mCallType = " + mCallType +
diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java
index 9369b20..56b8822 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.java
+++ b/telephony/java/com/android/ims/ImsReasonInfo.java
@@ -25,6 +25,7 @@
* @hide
*/
public class ImsReasonInfo implements Parcelable {
+
/**
* Specific code of each types
*/
@@ -284,6 +285,29 @@
public static final int CODE_EPDG_TUNNEL_LOST_CONNECTION = 1402;
/**
+ * The maximum number of calls allowed has been reached. Used in a multi-endpoint scenario
+ * where the number of calls across all connected devices has reached the maximum.
+ */
+ public static final int CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED = 1403;
+
+ /**
+ * Similar to {@link #CODE_LOCAL_CALL_DECLINE}, except indicates that a remote device has
+ * declined the call. Used in a multi-endpoint scenario where a remote device declined an
+ * incoming call.
+ */
+ public static final int CODE_REMOTE_CALL_DECLINE = 1404;
+
+ /**
+ * Indicates the call was disconnected due to the user reaching their data limit.
+ */
+ public static final int CODE_DATA_LIMIT_REACHED = 1405;
+
+ /**
+ * Indicates the call was disconnected due to the user disabling cellular data.
+ */
+ public static final int CODE_DATA_DISABLED = 1406;
+
+ /**
* Network string error messages.
* mExtraMessage may have these values.
*/
diff --git a/telephony/java/com/android/ims/internal/IImsService.aidl b/telephony/java/com/android/ims/internal/IImsService.aidl
index a9614a6..406d22d 100644
--- a/telephony/java/com/android/ims/internal/IImsService.aidl
+++ b/telephony/java/com/android/ims/internal/IImsService.aidl
@@ -38,8 +38,19 @@
void close(int serviceId);
boolean isConnected(int serviceId, int serviceType, int callType);
boolean isOpened(int serviceId);
+
+ /**
+ * Replace existing registration listener
+ *
+ */
void setRegistrationListener(int serviceId, in IImsRegistrationListener listener);
+ /**
+ * Add new registration listener
+ */
+ void addRegistrationListener(int phoneId, int serviceClass,
+ in IImsRegistrationListener listener);
+
ImsCallProfile createCallProfile(int serviceId, int serviceType, int callType);
IImsCallSession createCallSession(int serviceId, in ImsCallProfile profile,
diff --git a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
index ca7354f..8b81b0d 100644
--- a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
@@ -17,18 +17,23 @@
package com.android.internal.telephony;
import android.annotation.Nullable;
+import android.content.ContentResolver;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.RemoteException;
+import android.provider.Settings;
import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.SystemConfig;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* Utilities for handling carrier applications.
@@ -53,6 +58,11 @@
* in the default state (e.g. not explicitly DISABLED/DISABLED_BY_USER/ENABLED), or we enable if
* the app is carrier privileged and in either the default state or DISABLED_UNTIL_USED.
*
+ * In addition, there is a list of carrier-associated applications in
+ * {@link SystemConfig#getDisabledUntilUsedPreinstalledCarrierAssociatedApps}. Each app in this
+ * list is associated with a carrier app. When the given carrier app is enabled/disabled per the
+ * above, the associated applications are enabled/disabled to match.
+ *
* When enabling a carrier app we also grant it default permissions.
*
* This method is idempotent and is safe to be called at any time; it should be called once at
@@ -60,19 +70,24 @@
* privileged apps may have changed.
*/
public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage,
- IPackageManager packageManager, TelephonyManager telephonyManager, int userId) {
+ IPackageManager packageManager, TelephonyManager telephonyManager,
+ ContentResolver contentResolver, int userId) {
if (DEBUG) {
Slog.d(TAG, "disableCarrierAppsUntilPrivileged");
}
+ SystemConfig config = SystemConfig.getInstance();
String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
- disableCarrierAppsUntilPrivileged(callingPackage, packageManager, telephonyManager, userId,
- systemCarrierAppsDisabledUntilUsed);
+ ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed =
+ config.getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
+ disableCarrierAppsUntilPrivileged(callingPackage, packageManager, telephonyManager,
+ contentResolver, userId, systemCarrierAppsDisabledUntilUsed,
+ systemCarrierAssociatedAppsDisabledUntilUsed);
}
/**
* Like {@link #disableCarrierAppsUntilPrivileged(String, IPackageManager, TelephonyManager,
- * int)}, but assumes that no carrier apps have carrier privileges.
+ * ContentResolver, int)}, but assumes that no carrier apps have carrier privileges.
*
* This prevents a potential race condition on first boot - since the app's default state is
* enabled, we will initially disable it when the telephony stack is first initialized as it has
@@ -82,29 +97,43 @@
* Manager can kill it, and this can lead to crashes as the app is in an unexpected state.
*/
public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage,
- IPackageManager packageManager, int userId) {
+ IPackageManager packageManager, ContentResolver contentResolver, int userId) {
if (DEBUG) {
Slog.d(TAG, "disableCarrierAppsUntilPrivileged");
}
+ SystemConfig config = SystemConfig.getInstance();
String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
+ ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed =
+ config.getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
disableCarrierAppsUntilPrivileged(callingPackage, packageManager,
- null /* telephonyManager */, userId, systemCarrierAppsDisabledUntilUsed);
+ null /* telephonyManager */, contentResolver, userId,
+ systemCarrierAppsDisabledUntilUsed, systemCarrierAssociatedAppsDisabledUntilUsed);
}
// Must be public b/c framework unit tests can't access package-private methods.
@VisibleForTesting
public static void disableCarrierAppsUntilPrivileged(String callingPackage,
- IPackageManager packageManager, @Nullable TelephonyManager telephonyManager, int userId,
- String[] systemCarrierAppsDisabledUntilUsed) {
+ IPackageManager packageManager, @Nullable TelephonyManager telephonyManager,
+ ContentResolver contentResolver, int userId,
+ String[] systemCarrierAppsDisabledUntilUsed,
+ ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed) {
List<ApplicationInfo> candidates = getDefaultCarrierAppCandidatesHelper(packageManager,
userId, systemCarrierAppsDisabledUntilUsed);
if (candidates == null || candidates.isEmpty()) {
return;
}
+ Map<String, List<ApplicationInfo>> associatedApps = getDefaultCarrierAssociatedAppsHelper(
+ packageManager,
+ userId,
+ systemCarrierAssociatedAppsDisabledUntilUsed);
+
List<String> enabledCarrierPackages = new ArrayList<>();
+ boolean hasRunOnce = Settings.Secure.getIntForUser(
+ contentResolver, Settings.Secure.CARRIER_APPS_HANDLED, 0, userId) == 1;
+
try {
for (ApplicationInfo ai : candidates) {
String packageName = ai.packageName;
@@ -112,33 +141,92 @@
telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) ==
TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
- // Only update enabled state for the app on /system. Once it has been updated we
- // shouldn't touch it.
- if (!ai.isUpdatedSystemApp()) {
- if (hasPrivileges
- && (ai.enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+ if (hasPrivileges) {
+ // Only update enabled state for the app on /system. Once it has been
+ // updated we shouldn't touch it.
+ if (!ai.isUpdatedSystemApp()
+ && (ai.enabledSetting ==
+ PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| ai.enabledSetting ==
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
Slog.i(TAG, "Update state(" + packageName + "): ENABLED for user "
+ userId);
- packageManager.setApplicationEnabledSetting(packageName,
+ packageManager.setApplicationEnabledSetting(
+ packageName,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
- PackageManager.DONT_KILL_APP, userId, callingPackage);
- } else if (!hasPrivileges
+ PackageManager.DONT_KILL_APP,
+ userId,
+ callingPackage);
+ }
+
+ // Also enable any associated apps for this carrier app.
+ List<ApplicationInfo> associatedAppList = associatedApps.get(packageName);
+ if (associatedAppList != null) {
+ for (ApplicationInfo associatedApp : associatedAppList) {
+ if (associatedApp.enabledSetting ==
+ PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+ || associatedApp.enabledSetting ==
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
+ Slog.i(TAG, "Update associated state(" + associatedApp.packageName
+ + "): ENABLED for user " + userId);
+ packageManager.setApplicationEnabledSetting(
+ associatedApp.packageName,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+ PackageManager.DONT_KILL_APP,
+ userId,
+ callingPackage);
+ }
+ }
+ }
+
+ // Always re-grant default permissions to carrier apps w/ privileges.
+ enabledCarrierPackages.add(ai.packageName);
+ } else { // No carrier privileges
+ // Only update enabled state for the app on /system. Once it has been
+ // updated we shouldn't touch it.
+ if (!ai.isUpdatedSystemApp()
&& ai.enabledSetting ==
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
Slog.i(TAG, "Update state(" + packageName
+ "): DISABLED_UNTIL_USED for user " + userId);
- packageManager.setApplicationEnabledSetting(packageName,
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0,
- userId, callingPackage);
+ packageManager.setApplicationEnabledSetting(
+ packageName,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,
+ 0,
+ userId,
+ callingPackage);
+ }
+
+ // Also disable any associated apps for this carrier app if this is the first
+ // run. We avoid doing this a second time because it is brittle to rely on the
+ // distinction between "default" and "enabled".
+ if (!hasRunOnce) {
+ List<ApplicationInfo> associatedAppList = associatedApps.get(packageName);
+ if (associatedAppList != null) {
+ for (ApplicationInfo associatedApp : associatedAppList) {
+ if (associatedApp.enabledSetting
+ == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+ Slog.i(TAG,
+ "Update associated state(" + associatedApp.packageName
+ + "): DISABLED_UNTIL_USED for user " + userId);
+ packageManager.setApplicationEnabledSetting(
+ associatedApp.packageName,
+ PackageManager
+ .COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,
+ 0,
+ userId,
+ callingPackage);
+ }
+ }
+ }
}
}
+ }
- // Always re-grant default permissions to carrier apps w/ privileges.
- if (hasPrivileges) {
- enabledCarrierPackages.add(ai.packageName);
- }
+ // Mark the execution so we do not disable apps again.
+ if (!hasRunOnce) {
+ Settings.Secure.putIntForUser(
+ contentResolver, Settings.Secure.CARRIER_APPS_HANDLED, 1, userId);
}
if (!enabledCarrierPackages.isEmpty()) {
@@ -190,8 +278,8 @@
*
* These are the apps subject to the hiding/showing logic in
* {@link CarrierAppUtils#disableCarrierAppsUntilPrivileged(String, IPackageManager,
- * TelephonyManager, int)}, as well as the apps which should have default permissions granted,
- * when a matching SIM is inserted.
+ * TelephonyManager, ContentResolver, int)}, as well as the apps which should have default
+ * permissions granted, when a matching SIM is inserted.
*
* Whether or not the app is actually considered a default app depends on whether the app has
* carrier privileges as determined by the SIMs in the device.
@@ -205,30 +293,68 @@
}
private static List<ApplicationInfo> getDefaultCarrierAppCandidatesHelper(
- IPackageManager packageManager, int userId,
+ IPackageManager packageManager,
+ int userId,
String[] systemCarrierAppsDisabledUntilUsed) {
if (systemCarrierAppsDisabledUntilUsed == null
|| systemCarrierAppsDisabledUntilUsed.length == 0) {
return null;
}
- List<ApplicationInfo> apps = null;
- try {
- apps = new ArrayList<>(systemCarrierAppsDisabledUntilUsed.length);
- for (String packageName : systemCarrierAppsDisabledUntilUsed) {
- ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
- PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, userId);
- if (ai == null) {
- // No app found for packageName
- continue;
- }
- if (!ai.isSystemApp()) {
- continue;
- }
+ List<ApplicationInfo> apps = new ArrayList<>(systemCarrierAppsDisabledUntilUsed.length);
+ for (int i = 0; i < systemCarrierAppsDisabledUntilUsed.length; i++) {
+ String packageName = systemCarrierAppsDisabledUntilUsed[i];
+ ApplicationInfo ai =
+ getApplicationInfoIfSystemApp(packageManager, userId, packageName);
+ if (ai != null) {
apps.add(ai);
}
+ }
+ return apps;
+ }
+
+ private static Map<String, List<ApplicationInfo>> getDefaultCarrierAssociatedAppsHelper(
+ IPackageManager packageManager,
+ int userId,
+ ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed) {
+ int size = systemCarrierAssociatedAppsDisabledUntilUsed.size();
+ Map<String, List<ApplicationInfo>> associatedApps = new ArrayMap<>(size);
+ for (int i = 0; i < size; i++) {
+ String carrierAppPackage = systemCarrierAssociatedAppsDisabledUntilUsed.keyAt(i);
+ List<String> associatedAppPackages =
+ systemCarrierAssociatedAppsDisabledUntilUsed.valueAt(i);
+ for (int j = 0; j < associatedAppPackages.size(); j++) {
+ ApplicationInfo ai =
+ getApplicationInfoIfSystemApp(
+ packageManager, userId, associatedAppPackages.get(j));
+ // Only update enabled state for the app on /system. Once it has been updated we
+ // shouldn't touch it.
+ if (ai != null && !ai.isUpdatedSystemApp()) {
+ List<ApplicationInfo> appList = associatedApps.get(carrierAppPackage);
+ if (appList == null) {
+ appList = new ArrayList<>();
+ associatedApps.put(carrierAppPackage, appList);
+ }
+ appList.add(ai);
+ }
+ }
+ }
+ return associatedApps;
+ }
+
+ @Nullable
+ private static ApplicationInfo getApplicationInfoIfSystemApp(
+ IPackageManager packageManager,
+ int userId,
+ String packageName) {
+ try {
+ ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
+ PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, userId);
+ if (ai != null && ai.isSystemApp()) {
+ return ai;
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Could not reach PackageManager", e);
}
- return apps;
+ return null;
}
}
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index e4981ce..f01e4c0 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -104,6 +104,8 @@
public static final int CMD_CLEAR_PROVISIONING_SPINNER = BASE + 42;
public static final int EVENT_DEVICE_PROVISIONED_CHANGE = BASE + 43;
public static final int EVENT_REDIRECTION_DETECTED = BASE + 44;
+ public static final int EVENT_PCO_DATA_RECEIVED = BASE + 45;
+ public static final int EVENT_SET_CARRIER_DATA_ENABLED = BASE + 46;
/***** Constants *****/
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index 70a8653..6115656 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -184,7 +184,7 @@
*/
void sendTextForSubscriberWithSelfPermissions(in int subId, String callingPkg,
in String destAddr, in String scAddr, in String text, in PendingIntent sentIntent,
- in PendingIntent deliveryIntent);
+ in PendingIntent deliveryIntent, in boolean persistMessage);
/**
* Inject an SMS PDU into the android platform.
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index c61ed2a..f6aef08 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -118,17 +118,6 @@
int setDisplayName(String displayName, int subId);
/**
- * Set Sim Provisioning Status by subscription ID
- * @param simProvisionStatus with the subscription:
- * {@See SubscriptionManager#SIM_PROVISIONED}
- * {@See SubscriptionManager#SIM_UNPROVISIONED_COLD}
- * {@See SubscriptionManager#SIM_UNPROVISIONED_OUT_OF_CREDIT}
- * @param subId the unique SubInfoRecord index in database
- * @return the number of records updated
- */
- int setSimProvisioningStatus(int simProvisioningStatus, int subId);
-
- /**
* Set display name by simInfo index with name source
* @param displayName the display name of SIM card
* @param subId the unique SubscriptionInfo index in database
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index bb8aaad5..a8eaf36 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -20,6 +20,7 @@
import android.os.Bundle;
import android.os.ResultReceiver;
import android.net.Uri;
+import android.service.carrier.CarrierIdentifier;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telephony.CellInfo;
@@ -28,8 +29,11 @@
import android.telephony.NeighboringCellInfo;
import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
+import android.telephony.TelephonyHistogram;
+import android.telephony.VisualVoicemailSmsFilterSettings;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.OperatorInfo;
+
import java.util.List;
@@ -450,6 +454,26 @@
*/
int getVoiceMessageCountForSubscriber(int subId);
+ oneway void setVisualVoicemailEnabled(String callingPackage,
+ in PhoneAccountHandle accountHandle, boolean enabled);
+
+ boolean isVisualVoicemailEnabled(String callingPackage,
+ in PhoneAccountHandle accountHandle);
+
+ // Not oneway, caller needs to make sure the vaule is set before receiving a SMS
+ void enableVisualVoicemailSmsFilter(String callingPackage, int subId,
+ in VisualVoicemailSmsFilterSettings settings);
+
+ oneway void disableVisualVoicemailSmsFilter(String callingPackage, int subId);
+
+ // Get settings set by the calling package
+ VisualVoicemailSmsFilterSettings getVisualVoicemailSmsFilterSettings(String callingPackage,
+ int subId);
+
+ // Get settings set by the package, requires READ_PRIVILEGED_PHONE_STATE permission
+ VisualVoicemailSmsFilterSettings getSystemVisualVoicemailSmsFilterSettings(String packageName,
+ int subId);
+
/**
* Returns the network type for data transmission
* Legacy call, permission-free
@@ -1067,4 +1091,95 @@
* Returns a list of packages that have carrier privileges.
*/
List<String> getPackagesWithCarrierPrivileges();
+
+ /**
+ * Return the application ID for the app type.
+ *
+ * @param subId the subscription ID that this request applies to.
+ * @param appType the uicc app type,
+ * @return Application ID for specificied app type or null if no uicc or error.
+ */
+ String getAidForAppType(int subId, int appType);
+
+ /**
+ * Return the Electronic Serial Number.
+ *
+ * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+ *
+ * @param subId the subscription ID that this request applies to.
+ * @return ESN or null if error.
+ * @hide
+ */
+ String getEsn(int subId);
+
+ /**
+ * Return the Preferred Roaming List Version
+ *
+ * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+ * @param subId the subscription ID that this request applies to.
+ * @return PRLVersion or null if error.
+ * @hide
+ */
+ String getCdmaPrlVersion(int subId);
+
+ /**
+ * Get snapshot of Telephony histograms
+ * @return List of Telephony histograms
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges.
+ */
+ List<TelephonyHistogram> getTelephonyHistograms();
+
+ /**
+ * Set the allowed carrier list for slotId
+ * Require system privileges. In the future we may add this to carrier APIs.
+ *
+ * @return The number of carriers set successfully. Should match length of
+ * carriers on success.
+ */
+ int setAllowedCarriers(int slotId, in List<CarrierIdentifier> carriers);
+
+ /**
+ * Get the allowed carrier list for slotId.
+ * Require system privileges. In the future we may add this to carrier APIs.
+ *
+ * @return List of {@link android.service.carrier.CarrierIdentifier}; empty list
+ * means all carriers are allowed.
+ */
+ List<CarrierIdentifier> getAllowedCarriers(int slotId);
+
+ /**
+ * Action set from carrier signalling broadcast receivers to enable/disable metered apns
+ * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+ * @param subId the subscription ID that this action applies to.
+ * @param enabled control enable or disable metered apns.
+ * @hide
+ */
+ void carrierActionSetMeteredApnsEnabled(int subId, boolean visible);
+
+ /**
+ * Action set from carrier signalling broadcast receivers to enable/disable radio
+ * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+ * @param subId the subscription ID that this action applies to.
+ * @param enabled control enable or disable radio.
+ * @hide
+ */
+ void carrierActionSetRadioEnabled(int subId, boolean enabled);
+
+ /**
+ * Get aggregated video call data usage since boot.
+ * Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required.
+ * @return total data usage in bytes
+ * @hide
+ */
+ long getVtDataUsage();
+
+ /**
+ * Policy control of data connection. Usually used when data limit is passed.
+ * @param enabled True if enabling the data, otherwise disabling.
+ * @param subId Subscription index
+ * @hide
+ */
+ void setPolicyDataEnabled(boolean enabled, int subId);
}
diff --git a/telephony/java/com/android/internal/telephony/IccCardConstants.java b/telephony/java/com/android/internal/telephony/IccCardConstants.java
index c1e2518..f3d9335 100644
--- a/telephony/java/com/android/internal/telephony/IccCardConstants.java
+++ b/telephony/java/com/android/internal/telephony/IccCardConstants.java
@@ -32,6 +32,8 @@
public static final String INTENT_VALUE_ICC_ABSENT = "ABSENT";
/* CARD_IO_ERROR means for three consecutive times there was SIM IO error */
static public final String INTENT_VALUE_ICC_CARD_IO_ERROR = "CARD_IO_ERROR";
+ /* CARD_RESTRICTED means card is present but not usable due to carrier restrictions */
+ static public final String INTENT_VALUE_ICC_CARD_RESTRICTED = "CARD_RESTRICTED";
/* LOCKED means ICC is locked by pin or by network */
public static final String INTENT_VALUE_ICC_LOCKED = "LOCKED";
//TODO: we can remove this state in the future if Bug 18489776 analysis
@@ -74,7 +76,8 @@
READY, /** ordinal(5) == {@See TelephonyManager#SIM_STATE_READY} */
NOT_READY, /** ordinal(6) == {@See TelephonyManager#SIM_STATE_NOT_READY} */
PERM_DISABLED, /** ordinal(7) == {@See TelephonyManager#SIM_STATE_PERM_DISABLED} */
- CARD_IO_ERROR; /** ordinal(8) == {@See TelephonyManager#SIM_STATE_CARD_IO_ERROR} */
+ CARD_IO_ERROR, /** ordinal(8) == {@See TelephonyManager#SIM_STATE_CARD_IO_ERROR} */
+ CARD_RESTRICTED;/** ordinal(9) == {@See TelephonyManager#SIM_STATE_CARD_RESTRICTED} */
public boolean isPinLocked() {
return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED));
@@ -83,7 +86,8 @@
public boolean iccCardExist() {
return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED)
|| (this == NETWORK_LOCKED) || (this == READY)
- || (this == PERM_DISABLED) || (this == CARD_IO_ERROR));
+ || (this == PERM_DISABLED) || (this == CARD_IO_ERROR)
+ || (this == CARD_RESTRICTED));
}
public static State intToState(int state) throws IllegalArgumentException {
@@ -97,6 +101,7 @@
case 6: return NOT_READY;
case 7: return PERM_DISABLED;
case 8: return CARD_IO_ERROR;
+ case 9: return CARD_RESTRICTED;
default:
throw new IllegalArgumentException();
}
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index 1680fe3..b417a1c 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -208,4 +208,14 @@
public static final int AUTH_CONTEXT_EAP_SIM = 128;
public static final int AUTH_CONTEXT_EAP_AKA = 129;
public static final int AUTH_CONTEXT_UNDEFINED = -1;
+
+ /**
+ * Value for the global property CELL_ON
+ * 0: Cell radio is off
+ * 1: Cell radio is on
+ * 2: Cell radio is off because airplane mode is enabled
+ */
+ public static final int CELL_OFF_FLAG = 0;
+ public static final int CELL_ON_FLAG = 1;
+ public static final int CELL_OFF_DUE_TO_AIRPLANE_MODE_FLAG = 2;
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index cfc5305..a91e9be 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -409,6 +409,8 @@
int RIL_REQUEST_STOP_LCE = 133;
int RIL_REQUEST_PULL_LCEDATA = 134;
int RIL_REQUEST_GET_ACTIVITY_INFO = 135;
+ int RIL_REQUEST_SET_ALLOWED_CARRIERS = 136;
+ int RIL_REQUEST_GET_ALLOWED_CARRIERS = 137;
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
@@ -459,4 +461,5 @@
int RIL_UNSOL_ON_SS = 1043;
int RIL_UNSOL_STK_CC_ALPHA_NOTIFY = 1044;
int RIL_UNSOL_LCEDATA_RECV = 1045;
+ int RIL_UNSOL_PCO_DATA = 1046;
}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index 46b0fbd..0168874 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -402,34 +402,59 @@
/**
* <p>Broadcast Action: when data connections get redirected with validation failure.
* intended for sim/account status checks and only sent to the specified carrier app
- * feedback is via carrier/system APIs to report cold-sim, out-of-credit-sim, etc
* The intent will have the following extra values:</p>
* <ul>
- * <li>redirectUrl</li><dd>A string with the redirection url info.</dd>
- * <li>subId</li><dd>Sub Id which associated the data redirection.</dd>
+ * <li>apnType</li><dd>A string with the apn type.</dd>
+ * <li>redirectionUrl</li><dd>redirection url string</dd>
+ * <li>subId</dt><li>Sub Id which associated the data connection failure.</dd>
* </ul>
* <p class="note">This is a protected intent that can only be sent by the system.</p>
*/
- public static final String ACTION_DATA_CONNECTION_REDIRECTED =
- "android.intent.action.REDIRECTION_DETECTED";
+ public static final String ACTION_CARRIER_SIGNAL_REDIRECTED =
+ "android.intent.action.CARRIER_SIGNAL_REDIRECTED";
/**
* <p>Broadcast Action: when data connections setup fails.
* intended for sim/account status checks and only sent to the specified carrier app
- * feedback is via carrier/system APIs to report cold-sim, out-of-credit-sim, etc
* The intent will have the following extra values:</p>
* <ul>
* <li>apnType</li><dd>A string with the apn type.</dd>
* <li>errorCode</li><dd>A integer with dataFailCause.</dd>
- * <li>subId</dt><li>Sub Id which associated the data redirection.</dd>
+ * <li>subId</dt><li>Sub Id which associated the data connection failure.</dd>
* </ul>
* <p class="note">This is a protected intent that can only be sent by the system. </p>
*/
- public static final String ACTION_REQUEST_NETWORK_FAILED =
- "android.intent.action.REQUEST_NETWORK_FAILED";
+ public static final String ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED =
+ "android.intent.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED";
/**
- * Broadcast action to trigger CI OMA-DM Session.
+ * <p>Broadcast Action: when pco value is available.
+ * intended for sim/account status checks and only sent to the specified carrier app
+ * The intent will have the following extra values:</p>
+ * <ul>
+ * <li>apnType</li><dd>A string with the apn type.</dd>
+ * <li>apnProto</li><dd>A string with the protocol of the apn connection (IP,IPV6,
+ * IPV4V6)</dd>
+ * <li>pcoId</li><dd>An integer indicating the pco id for the data.</dd>
+ * <li>pcoValue</li><dd>A byte array of pco data read from modem.</dd>
+ * <li>subId</dt><li>Sub Id which associated the data connection.</dd>
+ * </ul>
+ * <p class="note">This is a protected intent that can only be sent by the system. </p>
*/
+ public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE =
+ "android.intent.action.CARRIER_SIGNAL_PCO_VALUE";
+
+ // CARRIER_SIGNAL_ACTION extra keys
+ public static final String EXTRA_REDIRECTION_URL_KEY = "redirectionUrl";
+ public static final String EXTRA_ERROR_CODE_KEY = "errorCode";
+ public static final String EXTRA_APN_TYPE_KEY = "apnType";
+ public static final String EXTRA_APN_PROTO_KEY = "apnProto";
+ public static final String EXTRA_PCO_ID_KEY = "pcoId";
+ public static final String EXTRA_PCO_VALUE_KEY = "pcoValue";
+
+
+ /**
+ * Broadcast action to trigger CI OMA-DM Session.
+ */
public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE =
"com.android.omadm.service.CONFIGURATION_UPDATE";
}
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 2346f85..16a0def 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -574,6 +574,7 @@
mLaunchIntent = intent;
mForceStopBeforeLaunch = forceStopBeforeLaunch;
mLaunchReason = launchReason;
+ mResult = -1L;
}
public Long getResult() {
diff --git a/tests/Assist/src/com/android/test/assist/AssistInteractionSession.java b/tests/Assist/src/com/android/test/assist/AssistInteractionSession.java
index 851bda9..0718628 100644
--- a/tests/Assist/src/com/android/test/assist/AssistInteractionSession.java
+++ b/tests/Assist/src/com/android/test/assist/AssistInteractionSession.java
@@ -77,6 +77,9 @@
} catch (InterruptedException e) {
e.printStackTrace();
}
+
+ getWindow().getWindow().getDecorView().setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
@Override
diff --git a/tests/SoundTriggerTestApp/AndroidManifest.xml b/tests/SoundTriggerTestApp/AndroidManifest.xml
index 71d6001..87f3e92 100644
--- a/tests/SoundTriggerTestApp/AndroidManifest.xml
+++ b/tests/SoundTriggerTestApp/AndroidManifest.xml
@@ -1,25 +1,28 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.test.soundtrigger">
-
+ <uses-permission android:name="android.permission.CAPTURE_AUDIO_HOTWORD" />
<uses-permission android:name="android.permission.MANAGE_SOUND_TRIGGER" />
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application>
<activity
- android:name="TestSoundTriggerActivity"
+ android:name=".SoundTriggerTestActivity"
android:label="SoundTrigger Test Application"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.Material">
- <!--
- <intent-filter>
- <action android:name="com.android.intent.action.MANAGE_SOUND_TRIGGER" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+ <service
+ android:name=".SoundTriggerTestService"
+ android:stopWithTask="false"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="com.android.intent.action.MANAGE_SOUND_TRIGGER" />
+ </intent-filter>
+ </service>
</application>
</manifest>
diff --git a/tests/SoundTriggerTestApp/res/layout/main.xml b/tests/SoundTriggerTestApp/res/layout/main.xml
index 06949a0..0fd8b12 100644
--- a/tests/SoundTriggerTestApp/res/layout/main.xml
+++ b/tests/SoundTriggerTestApp/res/layout/main.xml
@@ -18,81 +18,107 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
- >
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- >
+ android:orientation="vertical">
- <Button
- android:layout_width="wrap_content"
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/load"
+ android:onClick="onLoadButtonClicked"
+ android:padding="20dp" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/start_recog"
+ android:onClick="onStartRecognitionButtonClicked"
+ android:padding="20dp" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/stop_recog"
+ android:onClick="onStopRecognitionButtonClicked"
+ android:padding="20dp" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/unload"
+ android:onClick="onUnloadButtonClicked"
+ android:padding="20dp" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/reload"
+ android:onClick="onReloadButtonClicked"
+ android:padding="20dp" />
+
+ <Button
+ android:id="@+id/play_trigger_id"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/play_trigger"
+ android:onClick="onPlayTriggerButtonClicked"
+ android:padding="20dp" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/enroll"
- android:onClick="onEnrollButtonClicked"
- android:padding="20dp" />
+ android:layout_gravity="right">
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/reenroll"
- android:onClick="onReEnrollButtonClicked"
- android:padding="20dp" />
+ <CheckBox
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/capture"
+ android:id="@+id/caputre_check_box"
+ android:layout_gravity="center_horizontal"
+ android:padding="20dp" />
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/start_recog"
- android:onClick="onStartRecognitionButtonClicked"
- android:padding="20dp" />
+ <Button
+ android:id="@+id/play_captured_id"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/play_capture"
+ android:padding="20dp"
+ android:enabled="false" />
+ </LinearLayout>
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/stop_recog"
- android:onClick="onStopRecognitionButtonClicked"
- android:padding="20dp" />
-
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/unenroll"
- android:onClick="onUnEnrollButtonClicked"
- android:padding="20dp" />
-
- <Button
- android:id="@+id/play_trigger_id"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/play_trigger"
- android:onClick="onPlayTriggerButtonClicked"
- android:padding="20dp" />
-
-</LinearLayout>
-
-<RadioGroup xmlns:android="http://schemas.android.com/apk/res/android"
+ <RadioGroup xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/model_group_id"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="20dp"
- android:orientation="vertical">
-</RadioGroup>
+ android:orientation="vertical" />
-<ScrollView
+ <ScrollView
android:id="@+id/scroller_id"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:fillViewport="true">
- <TextView
- android:id="@+id/console"
- android:paddingTop="20pt"
- android:layout_height="fill_parent"
- android:layout_width="fill_parent"
- android:textSize="14dp"
- android:layout_weight="1.0"
- android:text="@string/none">
- </TextView>
-</ScrollView>
+ <TextView
+ android:id="@+id/console"
+ android:paddingTop="20pt"
+ android:layout_height="fill_parent"
+ android:layout_width="fill_parent"
+ android:textSize="14dp"
+ android:layout_weight="1.0"
+ android:text="@string/none" />
+ </ScrollView>
</LinearLayout>
diff --git a/tests/SoundTriggerTestApp/res/values/strings.xml b/tests/SoundTriggerTestApp/res/values/strings.xml
index 7c1f649..c48b648 100644
--- a/tests/SoundTriggerTestApp/res/values/strings.xml
+++ b/tests/SoundTriggerTestApp/res/values/strings.xml
@@ -16,11 +16,14 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="enroll">Load</string>
- <string name="reenroll">Re-load</string>
- <string name="unenroll">Un-load</string>
+ <string name="load">Load</string>
+ <string name="reload">Reload</string>
+ <string name="unload">Unload</string>
<string name="start_recog">Start</string>
<string name="stop_recog">Stop</string>
<string name="play_trigger">Play Trigger Audio</string>
+ <string name="capture">Capture Audio</string>
+ <string name="stop_capture">Stop Capturing Audio</string>
+ <string name="play_capture">Play Captured Audio</string>
<string name="none">Debug messages appear here:\n</string>
</resources>
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java
new file mode 100644
index 0000000..4841bc5
--- /dev/null
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.soundtrigger;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.text.Editable;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.test.soundtrigger.SoundTriggerTestService.SoundTriggerTestBinder;
+
+public class SoundTriggerTestActivity extends Activity implements SoundTriggerTestService.UserActivity {
+ private static final String TAG = "SoundTriggerTest";
+ private static final int AUDIO_PERMISSIONS_REQUEST = 1;
+
+ private SoundTriggerTestService mService = null;
+
+ private static UUID mSelectedModelUuid = null;
+
+ private Map<RadioButton, UUID> mButtonModelUuidMap;
+ private Map<UUID, RadioButton> mModelButtons;
+ private Map<UUID, String> mModelNames;
+ private List<RadioButton> mModelRadioButtons;
+
+ private TextView mDebugView = null;
+ private ScrollView mScrollView = null;
+ private Button mPlayTriggerButton = null;
+ private PowerManager.WakeLock mScreenWakelock;
+ private Handler mHandler;
+ private RadioGroup mRadioGroup;
+ private CheckBox mCaptureAudioCheckBox;
+ private Button mPlayCapturedAudioButton = null;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ // Make sure that this activity can punch through the lockscreen if needed.
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
+ WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ mDebugView = (TextView) findViewById(R.id.console);
+ mScrollView = (ScrollView) findViewById(R.id.scroller_id);
+ mRadioGroup = (RadioGroup) findViewById(R.id.model_group_id);
+ mPlayTriggerButton = (Button) findViewById(R.id.play_trigger_id);
+ mDebugView.setText(mDebugView.getText(), TextView.BufferType.EDITABLE);
+ mDebugView.setMovementMethod(new ScrollingMovementMethod());
+ mCaptureAudioCheckBox = (CheckBox) findViewById(R.id.caputre_check_box);
+ mPlayCapturedAudioButton = (Button) findViewById(R.id.play_captured_id);
+ mHandler = new Handler();
+ mButtonModelUuidMap = new HashMap();
+ mModelButtons = new HashMap();
+ mModelNames = new HashMap();
+ mModelRadioButtons = new LinkedList();
+
+ setVolumeControlStream(AudioManager.STREAM_MUSIC);
+
+ // Make sure that the service is started, so even if our activity goes down, we'll still
+ // have a request for it to run.
+ startService(new Intent(getBaseContext(), SoundTriggerTestService.class));
+
+ // Bind to SoundTriggerTestService.
+ Intent intent = new Intent(this, SoundTriggerTestService.class);
+ bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ // Unbind from the service.
+ if (mService != null) {
+ mService.setUserActivity(null);
+ unbindService(mConnection);
+ }
+ }
+
+ @Override
+ public void addModel(UUID modelUuid, String name) {
+ // Create a new widget for this model, and insert everything we'd need into the map.
+ RadioButton button = new RadioButton(this);
+ mModelRadioButtons.add(button);
+ button.setText(name);
+ button.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ onRadioButtonClicked(v);
+ }
+ });
+ mButtonModelUuidMap.put(button, modelUuid);
+ mModelButtons.put(modelUuid, button);
+ mModelNames.put(modelUuid, name);
+
+ // Sort all the radio buttons by name, then push them into the group in order.
+ Collections.sort(mModelRadioButtons, new Comparator<RadioButton>(){
+ @Override
+ public int compare(RadioButton button0, RadioButton button1) {
+ return button0.getText().toString().compareTo(button1.getText().toString());
+ }
+ });
+ mRadioGroup.removeAllViews();
+ for (View v : mModelRadioButtons) {
+ mRadioGroup.addView(v);
+ }
+
+ // If we don't have something selected, select this first thing.
+ if (mSelectedModelUuid == null || mSelectedModelUuid.equals(modelUuid)) {
+ button.setChecked(true);
+ onRadioButtonClicked(button);
+ }
+ }
+
+ @Override
+ public void setModelState(UUID modelUuid, String state) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ String newButtonText = mModelNames.get(modelUuid);
+ if (state != null) {
+ newButtonText += ": " + state;
+ }
+ mModelButtons.get(modelUuid).setText(newButtonText);
+ updateSelectModelSpecificUiElements();
+ }
+ });
+ }
+
+ @Override
+ public void showMessage(String msg, boolean showToast) {
+ // Append the message to the text field, then show the toast if requested.
+ this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ ((Editable) mDebugView.getText()).append(msg + "\n");
+ mScrollView.post(new Runnable() {
+ public void run() {
+ mScrollView.smoothScrollTo(0, mDebugView.getBottom());
+ }
+ });
+ if (showToast) {
+ Toast.makeText(SoundTriggerTestActivity.this, msg, Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void handleDetection(UUID modelUuid) {
+ screenWakeup();
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ screenRelease();
+ }
+ }, 1000L);
+ }
+
+ private void screenWakeup() {
+ if (mScreenWakelock == null) {
+ PowerManager pm = ((PowerManager)getSystemService(POWER_SERVICE));
+ mScreenWakelock = pm.newWakeLock(
+ PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, TAG);
+ }
+ mScreenWakelock.acquire();
+ }
+
+ private void screenRelease() {
+ mScreenWakelock.release();
+ }
+
+ public void onLoadButtonClicked(View v) {
+ if (mService == null) {
+ Log.e(TAG, "Could not load sound model: not bound to SoundTriggerTestService");
+ } else {
+ mService.loadModel(mSelectedModelUuid);
+ }
+ }
+
+ public void onUnloadButtonClicked(View v) {
+ if (mService == null) {
+ Log.e(TAG, "Can't unload model: not bound to SoundTriggerTestService");
+ } else {
+ mService.unloadModel(mSelectedModelUuid);
+ }
+ }
+
+ public void onReloadButtonClicked(View v) {
+ if (mService == null) {
+ Log.e(TAG, "Can't reload model: not bound to SoundTriggerTestService");
+ } else {
+ mService.reloadModel(mSelectedModelUuid);
+ }
+ }
+
+ public void onStartRecognitionButtonClicked(View v) {
+ if (mService == null) {
+ Log.e(TAG, "Can't start recognition: not bound to SoundTriggerTestService");
+ } else {
+ mService.startRecognition(mSelectedModelUuid);
+ }
+ }
+
+ public void onStopRecognitionButtonClicked(View v) {
+ if (mService == null) {
+ Log.e(TAG, "Can't stop recognition: not bound to SoundTriggerTestService");
+ } else {
+ mService.stopRecognition(mSelectedModelUuid);
+ }
+ }
+
+ public synchronized void onPlayTriggerButtonClicked(View v) {
+ if (mService == null) {
+ Log.e(TAG, "Can't play trigger audio: not bound to SoundTriggerTestService");
+ } else {
+ mService.playTriggerAudio(mSelectedModelUuid);
+ }
+ }
+
+ public synchronized void onCaptureAudioCheckboxClicked(View v) {
+ // See if we have the right permissions
+ if (!mService.hasMicrophonePermission()) {
+ requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO},
+ AUDIO_PERMISSIONS_REQUEST);
+ return;
+ } else {
+ mService.setCaptureAudio(mSelectedModelUuid, mCaptureAudioCheckBox.isChecked());
+ }
+ }
+
+ @Override
+ public synchronized void onRequestPermissionsResult(int requestCode, String permissions[],
+ int[] grantResults) {
+ if (requestCode == AUDIO_PERMISSIONS_REQUEST) {
+ if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
+ // Make sure that the check box is set to false.
+ mCaptureAudioCheckBox.setChecked(false);
+ }
+ mService.setCaptureAudio(mSelectedModelUuid, mCaptureAudioCheckBox.isChecked());
+ }
+ }
+
+ public synchronized void onPlayCapturedAudioButtonClicked(View v) {
+ if (mService == null) {
+ Log.e(TAG, "Can't play captured audio: not bound to SoundTriggerTestService");
+ } else {
+ mService.playCapturedAudio(mSelectedModelUuid);
+ }
+ }
+
+ public synchronized void onRadioButtonClicked(View view) {
+ // Is the button now checked?
+ boolean checked = ((RadioButton) view).isChecked();
+ if (checked) {
+ mSelectedModelUuid = mButtonModelUuidMap.get(view);
+ showMessage("Selected " + mModelNames.get(mSelectedModelUuid), false);
+ updateSelectModelSpecificUiElements();
+ }
+ }
+
+ private synchronized void updateSelectModelSpecificUiElements() {
+ // Set the play trigger button to be enabled only if we actually have some audio.
+ mPlayTriggerButton.setEnabled(mService.modelHasTriggerAudio((mSelectedModelUuid)));
+ // Similar logic for the captured audio.
+ mCaptureAudioCheckBox.setChecked(
+ mService.modelWillCaptureTriggerAudio(mSelectedModelUuid));
+ mPlayCapturedAudioButton.setEnabled(mService.modelHasCapturedAudio((mSelectedModelUuid)));
+ }
+
+ private ServiceConnection mConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ synchronized (SoundTriggerTestActivity.this) {
+ // We've bound to LocalService, cast the IBinder and get LocalService instance
+ SoundTriggerTestBinder binder = (SoundTriggerTestBinder) service;
+ mService = binder.getService();
+ mService.setUserActivity(SoundTriggerTestActivity.this);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName arg0) {
+ synchronized (SoundTriggerTestActivity.this) {
+ mService.setUserActivity(null);
+ mService = null;
+ }
+ }
+ };
+}
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
new file mode 100644
index 0000000..a2385d6
--- /dev/null
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
@@ -0,0 +1,720 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.soundtrigger;
+
+import android.Manifest;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
+import android.media.AudioAttributes;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioRecord;
+import android.media.AudioTrack;
+import android.media.MediaPlayer;
+import android.media.soundtrigger.SoundTriggerDetector;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Random;
+import java.util.UUID;
+
+public class SoundTriggerTestService extends Service {
+ private static final String TAG = "SoundTriggerTestSrv";
+ private static final String INTENT_ACTION = "com.android.intent.action.MANAGE_SOUND_TRIGGER";
+
+ // Binder given to clients.
+ private final IBinder mBinder;
+ private final Map<UUID, ModelInfo> mModelInfoMap;
+ private SoundTriggerUtil mSoundTriggerUtil;
+ private Random mRandom;
+ private UserActivity mUserActivity;
+
+ public interface UserActivity {
+ void addModel(UUID modelUuid, String state);
+ void setModelState(UUID modelUuid, String state);
+ void showMessage(String msg, boolean showToast);
+ void handleDetection(UUID modelUuid);
+ }
+
+ public SoundTriggerTestService() {
+ super();
+ mRandom = new Random();
+ mModelInfoMap = new HashMap();
+ mBinder = new SoundTriggerTestBinder();
+ }
+
+ @Override
+ public synchronized int onStartCommand(Intent intent, int flags, int startId) {
+ if (mModelInfoMap.isEmpty()) {
+ mSoundTriggerUtil = new SoundTriggerUtil(this);
+ loadModelsInDataDir();
+ }
+
+ // If we get killed, after returning from here, restart
+ return START_STICKY;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(INTENT_ACTION);
+ registerReceiver(mBroadcastReceiver, filter);
+
+ // Make sure the data directory exists, and we're the owner of it.
+ try {
+ getFilesDir().mkdir();
+ } catch (Exception e) {
+ // Don't care - we either made it, or it already exists.
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ stopAllRecognitions();
+ unregisterReceiver(mBroadcastReceiver);
+ }
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent != null && INTENT_ACTION.equals(intent.getAction())) {
+ String command = intent.getStringExtra("command");
+ if (command == null) {
+ Log.e(TAG, "No 'command' specified in " + INTENT_ACTION);
+ } else {
+ try {
+ if (command.equals("load")) {
+ loadModel(getModelUuidFromIntent(intent));
+ } else if (command.equals("unload")) {
+ unloadModel(getModelUuidFromIntent(intent));
+ } else if (command.equals("start")) {
+ startRecognition(getModelUuidFromIntent(intent));
+ } else if (command.equals("stop")) {
+ stopRecognition(getModelUuidFromIntent(intent));
+ } else if (command.equals("play_trigger")) {
+ playTriggerAudio(getModelUuidFromIntent(intent));
+ } else if (command.equals("play_captured")) {
+ playCapturedAudio(getModelUuidFromIntent(intent));
+ } else if (command.equals("set_capture")) {
+ setCaptureAudio(getModelUuidFromIntent(intent),
+ intent.getBooleanExtra("enabled", true));
+ } else if (command.equals("set_capture_timeout")) {
+ setCaptureAudioTimeout(getModelUuidFromIntent(intent),
+ intent.getIntExtra("timeout", 5000));
+ } else {
+ Log.e(TAG, "Unknown command '" + command + "'");
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to process " + command, e);
+ }
+ }
+ }
+ }
+ };
+
+ private UUID getModelUuidFromIntent(Intent intent) {
+ // First, see if the specified the UUID straight up.
+ String value = intent.getStringExtra("modelUuid");
+ if (value != null) {
+ return UUID.fromString(value);
+ }
+
+ // If they specified a name, use that to iterate through the map of models and find it.
+ value = intent.getStringExtra("name");
+ if (value != null) {
+ for (ModelInfo modelInfo : mModelInfoMap.values()) {
+ if (value.equals(modelInfo.name)) {
+ return modelInfo.modelUuid;
+ }
+ }
+ Log.e(TAG, "Failed to find a matching model with name '" + value + "'");
+ }
+
+ // We couldn't figure out what they were asking for.
+ throw new RuntimeException("Failed to get model from intent - specify either " +
+ "'modelUuid' or 'name'");
+ }
+
+ /**
+ * Will be called when the service is killed (through swipe aways, not if we're force killed).
+ */
+ @Override
+ public void onTaskRemoved(Intent rootIntent) {
+ super.onTaskRemoved(rootIntent);
+ stopAllRecognitions();
+ stopSelf();
+ }
+
+ @Override
+ public synchronized IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ public class SoundTriggerTestBinder extends Binder {
+ SoundTriggerTestService getService() {
+ // Return instance of our parent so clients can call public methods.
+ return SoundTriggerTestService.this;
+ }
+ }
+
+ public synchronized void setUserActivity(UserActivity activity) {
+ mUserActivity = activity;
+ if (mUserActivity != null) {
+ for (Map.Entry<UUID, ModelInfo> entry : mModelInfoMap.entrySet()) {
+ mUserActivity.addModel(entry.getKey(), entry.getValue().name);
+ mUserActivity.setModelState(entry.getKey(), entry.getValue().state);
+ }
+ }
+ }
+
+ private synchronized void stopAllRecognitions() {
+ for (ModelInfo modelInfo : mModelInfoMap.values()) {
+ if (modelInfo.detector != null) {
+ Log.i(TAG, "Stopping recognition for " + modelInfo.name);
+ try {
+ modelInfo.detector.stopRecognition();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to stop recognition", e);
+ }
+ }
+ }
+ }
+
+ // Helper struct for holding information about a model.
+ public static class ModelInfo {
+ public String name;
+ public String state;
+ public UUID modelUuid;
+ public UUID vendorUuid;
+ public MediaPlayer triggerAudioPlayer;
+ public SoundTriggerDetector detector;
+ public byte modelData[];
+ public boolean captureAudio;
+ public int captureAudioMs;
+ public AudioTrack captureAudioTrack;
+ }
+
+ private GenericSoundModel createNewSoundModel(ModelInfo modelInfo) {
+ return new GenericSoundModel(modelInfo.modelUuid, modelInfo.vendorUuid,
+ modelInfo.modelData);
+ }
+
+ public synchronized void loadModel(UUID modelUuid) {
+ ModelInfo modelInfo = mModelInfoMap.get(modelUuid);
+ if (modelInfo == null) {
+ postError("Could not find model for: " + modelUuid.toString());
+ return;
+ }
+
+ postMessage("Loading model: " + modelInfo.name);
+
+ GenericSoundModel soundModel = createNewSoundModel(modelInfo);
+
+ boolean status = mSoundTriggerUtil.addOrUpdateSoundModel(soundModel);
+ if (status) {
+ postToast("Successfully loaded " + modelInfo.name + ", UUID=" + soundModel.uuid);
+ setModelState(modelInfo, "Loaded");
+ } else {
+ postErrorToast("Failed to load " + modelInfo.name + ", UUID=" + soundModel.uuid + "!");
+ setModelState(modelInfo, "Failed to load");
+ }
+ }
+
+ public synchronized void unloadModel(UUID modelUuid) {
+ ModelInfo modelInfo = mModelInfoMap.get(modelUuid);
+ if (modelInfo == null) {
+ postError("Could not find model for: " + modelUuid.toString());
+ return;
+ }
+
+ postMessage("Unloading model: " + modelInfo.name);
+
+ GenericSoundModel soundModel = mSoundTriggerUtil.getSoundModel(modelUuid);
+ if (soundModel == null) {
+ postErrorToast("Sound model not found for " + modelInfo.name + "!");
+ return;
+ }
+ modelInfo.detector = null;
+ boolean status = mSoundTriggerUtil.deleteSoundModel(modelUuid);
+ if (status) {
+ postToast("Successfully unloaded " + modelInfo.name + ", UUID=" + soundModel.uuid);
+ setModelState(modelInfo, "Unloaded");
+ } else {
+ postErrorToast("Failed to unload " +
+ modelInfo.name + ", UUID=" + soundModel.uuid + "!");
+ setModelState(modelInfo, "Failed to unload");
+ }
+ }
+
+ public synchronized void reloadModel(UUID modelUuid) {
+ ModelInfo modelInfo = mModelInfoMap.get(modelUuid);
+ if (modelInfo == null) {
+ postError("Could not find model for: " + modelUuid.toString());
+ return;
+ }
+ postMessage("Reloading model: " + modelInfo.name);
+ GenericSoundModel soundModel = mSoundTriggerUtil.getSoundModel(modelUuid);
+ if (soundModel == null) {
+ postErrorToast("Sound model not found for " + modelInfo.name + "!");
+ return;
+ }
+ GenericSoundModel updated = createNewSoundModel(modelInfo);
+ boolean status = mSoundTriggerUtil.addOrUpdateSoundModel(updated);
+ if (status) {
+ postToast("Successfully reloaded " + modelInfo.name + ", UUID=" + modelInfo.modelUuid);
+ setModelState(modelInfo, "Reloaded");
+ } else {
+ postErrorToast("Failed to reload "
+ + modelInfo.name + ", UUID=" + modelInfo.modelUuid + "!");
+ setModelState(modelInfo, "Failed to reload");
+ }
+ }
+
+ public synchronized void startRecognition(UUID modelUuid) {
+ ModelInfo modelInfo = mModelInfoMap.get(modelUuid);
+ if (modelInfo == null) {
+ postError("Could not find model for: " + modelUuid.toString());
+ return;
+ }
+
+ if (modelInfo.detector == null) {
+ postMessage("Creating SoundTriggerDetector for " + modelInfo.name);
+ modelInfo.detector = mSoundTriggerUtil.createSoundTriggerDetector(
+ modelUuid, new DetectorCallback(modelInfo));
+ }
+
+ postMessage("Starting recognition for " + modelInfo.name + ", UUID=" + modelInfo.modelUuid);
+ if (modelInfo.detector.startRecognition(modelInfo.captureAudio ?
+ SoundTriggerDetector.RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO :
+ SoundTriggerDetector.RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS)) {
+ setModelState(modelInfo, "Started");
+ } else {
+ postErrorToast("Fast failure attempting to start recognition for " +
+ modelInfo.name + ", UUID=" + modelInfo.modelUuid);
+ setModelState(modelInfo, "Failed to start");
+ }
+ }
+
+ public synchronized void stopRecognition(UUID modelUuid) {
+ ModelInfo modelInfo = mModelInfoMap.get(modelUuid);
+ if (modelInfo == null) {
+ postError("Could not find model for: " + modelUuid.toString());
+ return;
+ }
+
+ if (modelInfo.detector == null) {
+ postErrorToast("Stop called on null detector for " +
+ modelInfo.name + ", UUID=" + modelInfo.modelUuid);
+ return;
+ }
+ postMessage("Triggering stop recognition for " +
+ modelInfo.name + ", UUID=" + modelInfo.modelUuid);
+ if (modelInfo.detector.stopRecognition()) {
+ setModelState(modelInfo, "Stopped");
+ } else {
+ postErrorToast("Fast failure attempting to stop recognition for " +
+ modelInfo.name + ", UUID=" + modelInfo.modelUuid);
+ setModelState(modelInfo, "Failed to stop");
+ }
+ }
+
+ public synchronized void playTriggerAudio(UUID modelUuid) {
+ ModelInfo modelInfo = mModelInfoMap.get(modelUuid);
+ if (modelInfo == null) {
+ postError("Could not find model for: " + modelUuid.toString());
+ return;
+ }
+ if (modelInfo.triggerAudioPlayer != null) {
+ postMessage("Playing trigger audio for " + modelInfo.name);
+ modelInfo.triggerAudioPlayer.start();
+ } else {
+ postMessage("No trigger audio for " + modelInfo.name);
+ }
+ }
+
+ public synchronized void playCapturedAudio(UUID modelUuid) {
+ ModelInfo modelInfo = mModelInfoMap.get(modelUuid);
+ if (modelInfo == null) {
+ postError("Could not find model for: " + modelUuid.toString());
+ return;
+ }
+ if (modelInfo.captureAudioTrack != null) {
+ postMessage("Playing captured audio for " + modelInfo.name);
+ modelInfo.captureAudioTrack.stop();
+ modelInfo.captureAudioTrack.reloadStaticData();
+ modelInfo.captureAudioTrack.play();
+ } else {
+ postMessage("No captured audio for " + modelInfo.name);
+ }
+ }
+
+ public synchronized void setCaptureAudioTimeout(UUID modelUuid, int captureTimeoutMs) {
+ ModelInfo modelInfo = mModelInfoMap.get(modelUuid);
+ if (modelInfo == null) {
+ postError("Could not find model for: " + modelUuid.toString());
+ return;
+ }
+ modelInfo.captureAudioMs = captureTimeoutMs;
+ Log.i(TAG, "Set " + modelInfo.name + " capture audio timeout to " +
+ captureTimeoutMs + "ms");
+ }
+
+ public synchronized void setCaptureAudio(UUID modelUuid, boolean captureAudio) {
+ ModelInfo modelInfo = mModelInfoMap.get(modelUuid);
+ if (modelInfo == null) {
+ postError("Could not find model for: " + modelUuid.toString());
+ return;
+ }
+ modelInfo.captureAudio = captureAudio;
+ Log.i(TAG, "Set " + modelInfo.name + " capture audio to " + captureAudio);
+ }
+
+ public synchronized boolean hasMicrophonePermission() {
+ return getBaseContext().checkSelfPermission(Manifest.permission.RECORD_AUDIO)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ public synchronized boolean modelHasTriggerAudio(UUID modelUuid) {
+ ModelInfo modelInfo = mModelInfoMap.get(modelUuid);
+ return modelInfo != null && modelInfo.triggerAudioPlayer != null;
+ }
+
+ public synchronized boolean modelWillCaptureTriggerAudio(UUID modelUuid) {
+ ModelInfo modelInfo = mModelInfoMap.get(modelUuid);
+ return modelInfo != null && modelInfo.captureAudio;
+ }
+
+ public synchronized boolean modelHasCapturedAudio(UUID modelUuid) {
+ ModelInfo modelInfo = mModelInfoMap.get(modelUuid);
+ return modelInfo != null && modelInfo.captureAudioTrack != null;
+ }
+
+ private void loadModelsInDataDir() {
+ // Load all the models in the data dir.
+ boolean loadedModel = false;
+ for (File file : getFilesDir().listFiles()) {
+ // Find meta-data in .properties files, ignore everything else.
+ if (!file.getName().endsWith(".properties")) {
+ continue;
+ }
+ try {
+ Properties properties = new Properties();
+ properties.load(new FileInputStream(file));
+ createModelInfo(properties);
+ loadedModel = true;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to load properties file " + file.getName());
+ }
+ }
+
+ // Create a few dummy models if we didn't load anything.
+ if (!loadedModel) {
+ Properties dummyModelProperties = new Properties();
+ for (String name : new String[]{"1", "2", "3"}) {
+ dummyModelProperties.setProperty("name", "Model " + name);
+ createModelInfo(dummyModelProperties);
+ }
+ }
+ }
+
+ /** Parses a Properties collection to generate a sound model.
+ *
+ * Missing keys are filled in with default/random values.
+ * @param properties Has the required 'name' property, but the remaining 'modelUuid',
+ * 'vendorUuid', 'triggerAudio', and 'dataFile' optional properties.
+ *
+ */
+ private synchronized void createModelInfo(Properties properties) {
+ try {
+ ModelInfo modelInfo = new ModelInfo();
+
+ if (!properties.containsKey("name")) {
+ throw new RuntimeException("must have a 'name' property");
+ }
+ modelInfo.name = properties.getProperty("name");
+
+ if (properties.containsKey("modelUuid")) {
+ modelInfo.modelUuid = UUID.fromString(properties.getProperty("modelUuid"));
+ } else {
+ modelInfo.modelUuid = UUID.randomUUID();
+ }
+
+ if (properties.containsKey("vendorUuid")) {
+ modelInfo.vendorUuid = UUID.fromString(properties.getProperty("vendorUuid"));
+ } else {
+ modelInfo.vendorUuid = UUID.randomUUID();
+ }
+
+ if (properties.containsKey("triggerAudio")) {
+ modelInfo.triggerAudioPlayer = MediaPlayer.create(this, Uri.parse(
+ getFilesDir().getPath() + "/" + properties.getProperty("triggerAudio")));
+ if (modelInfo.triggerAudioPlayer.getDuration() == 0) {
+ modelInfo.triggerAudioPlayer.release();
+ modelInfo.triggerAudioPlayer = null;
+ }
+ }
+
+ if (properties.containsKey("dataFile")) {
+ File modelDataFile = new File(
+ getFilesDir().getPath() + "/" + properties.getProperty("dataFile"));
+ modelInfo.modelData = new byte[(int) modelDataFile.length()];
+ FileInputStream input = new FileInputStream(modelDataFile);
+ input.read(modelInfo.modelData, 0, modelInfo.modelData.length);
+ } else {
+ modelInfo.modelData = new byte[1024];
+ mRandom.nextBytes(modelInfo.modelData);
+ }
+
+ modelInfo.captureAudioMs = Integer.parseInt((String) properties.getOrDefault(
+ "captureAudioDurationMs", "5000"));
+
+ // TODO: Add property support for keyphrase models when they're exposed by the
+ // service.
+
+ // Update our maps containing the button -> id and id -> modelInfo.
+ mModelInfoMap.put(modelInfo.modelUuid, modelInfo);
+ if (mUserActivity != null) {
+ mUserActivity.addModel(modelInfo.modelUuid, modelInfo.name);
+ mUserActivity.setModelState(modelInfo.modelUuid, modelInfo.state);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Error parsing properties for " + properties.getProperty("name"), e);
+ }
+ }
+
+ private class CaptureAudioRecorder implements Runnable {
+ private final ModelInfo mModelInfo;
+ private final SoundTriggerDetector.EventPayload mEvent;
+
+ public CaptureAudioRecorder(ModelInfo modelInfo, SoundTriggerDetector.EventPayload event) {
+ mModelInfo = modelInfo;
+ mEvent = event;
+ }
+
+ @Override
+ public void run() {
+ AudioFormat format = mEvent.getCaptureAudioFormat();
+ if (format == null) {
+ postErrorToast("No audio format in recognition event.");
+ return;
+ }
+
+ AudioRecord audioRecord = null;
+ AudioTrack playbackTrack = null;
+ try {
+ // Inform the audio flinger that we really do want the stream from the soundtrigger.
+ AudioAttributes.Builder attributesBuilder = new AudioAttributes.Builder();
+ attributesBuilder.setInternalCapturePreset(1999);
+ AudioAttributes attributes = attributesBuilder.build();
+
+ // Make sure we understand this kind of playback so we know how many bytes to read.
+ String encoding;
+ int bytesPerSample;
+ switch (format.getEncoding()) {
+ case AudioFormat.ENCODING_PCM_8BIT:
+ encoding = "8bit";
+ bytesPerSample = 1;
+ break;
+ case AudioFormat.ENCODING_PCM_16BIT:
+ encoding = "16bit";
+ bytesPerSample = 2;
+ break;
+ case AudioFormat.ENCODING_PCM_FLOAT:
+ encoding = "float";
+ bytesPerSample = 4;
+ break;
+ default:
+ throw new RuntimeException("Unhandled audio format in event");
+ }
+
+ int bytesRequired = format.getSampleRate() * format.getChannelCount() *
+ bytesPerSample * mModelInfo.captureAudioMs / 1000;
+ int minBufferSize = AudioRecord.getMinBufferSize(
+ format.getSampleRate(), format.getChannelMask(), format.getEncoding());
+ if (minBufferSize > bytesRequired) {
+ bytesRequired = minBufferSize;
+ }
+
+ // Make an AudioTrack so we can play the data back out after it's finished
+ // recording.
+ try {
+ int channelConfig = AudioFormat.CHANNEL_OUT_MONO;
+ if (format.getChannelCount() == 2) {
+ channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
+ } else if (format.getChannelCount() >= 3) {
+ throw new RuntimeException(
+ "Too many channels in captured audio for playback");
+ }
+
+ playbackTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
+ format.getSampleRate(), channelConfig, format.getEncoding(),
+ bytesRequired, AudioTrack.MODE_STATIC);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating playback track", e);
+ postErrorToast("Failed to create playback track: " + e.getMessage());
+ }
+
+ audioRecord = new AudioRecord(attributes, format, bytesRequired,
+ mEvent.getCaptureSession());
+
+ byte[] buffer = new byte[bytesRequired];
+
+ // Create a file so we can save the output data there for analysis later.
+ FileOutputStream fos = null;
+ try {
+ fos = new FileOutputStream( new File(
+ getFilesDir() + File.separator + mModelInfo.name.replace(' ', '_') +
+ "_capture_" + format.getChannelCount() + "ch_" +
+ format.getSampleRate() + "hz_" + encoding + ".pcm"));
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to open output for saving PCM data", e);
+ postErrorToast("Failed to open output for saving PCM data: " + e.getMessage());
+ }
+
+ // Inform the user we're recording.
+ setModelState(mModelInfo, "Recording");
+ audioRecord.startRecording();
+ while (bytesRequired > 0) {
+ int bytesRead = audioRecord.read(buffer, 0, buffer.length);
+ if (bytesRead == -1) {
+ break;
+ }
+ if (fos != null) {
+ fos.write(buffer, 0, bytesRead);
+ }
+ if (playbackTrack != null) {
+ playbackTrack.write(buffer, 0, bytesRead);
+ }
+ bytesRequired -= bytesRead;
+ }
+ audioRecord.stop();
+ } catch (Exception e) {
+ Log.e(TAG, "Error recording trigger audio", e);
+ postErrorToast("Error recording trigger audio: " + e.getMessage());
+ } finally {
+ if (audioRecord != null) {
+ audioRecord.release();
+ }
+ synchronized (SoundTriggerTestService.this) {
+ if (mModelInfo.captureAudioTrack != null) {
+ mModelInfo.captureAudioTrack.release();
+ }
+ mModelInfo.captureAudioTrack = playbackTrack;
+ }
+ setModelState(mModelInfo, "Recording finished");
+ }
+ }
+ }
+
+ // Implementation of SoundTriggerDetector.Callback.
+ private class DetectorCallback extends SoundTriggerDetector.Callback {
+ private final ModelInfo mModelInfo;
+
+ public DetectorCallback(ModelInfo modelInfo) {
+ mModelInfo = modelInfo;
+ }
+
+ public void onAvailabilityChanged(int status) {
+ postMessage(mModelInfo.name + " availability changed to: " + status);
+ }
+
+ public void onDetected(SoundTriggerDetector.EventPayload event) {
+ postMessage(mModelInfo.name + " onDetected(): " + eventPayloadToString(event));
+ synchronized (SoundTriggerTestService.this) {
+ if (mUserActivity != null) {
+ mUserActivity.handleDetection(mModelInfo.modelUuid);
+ }
+ if (mModelInfo.captureAudio) {
+ new Thread(new CaptureAudioRecorder(mModelInfo, event)).start();
+ }
+ }
+ }
+
+ public void onError() {
+ postMessage(mModelInfo.name + " onError()");
+ setModelState(mModelInfo, "Error");
+ }
+
+ public void onRecognitionPaused() {
+ postMessage(mModelInfo.name + " onRecognitionPaused()");
+ setModelState(mModelInfo, "Paused");
+ }
+
+ public void onRecognitionResumed() {
+ postMessage(mModelInfo.name + " onRecognitionResumed()");
+ setModelState(mModelInfo, "Resumed");
+ }
+ }
+
+ private String eventPayloadToString(SoundTriggerDetector.EventPayload event) {
+ String result = "EventPayload(";
+ AudioFormat format = event.getCaptureAudioFormat();
+ result = result + "AudioFormat: " + ((format == null) ? "null" : format.toString());
+ byte[] triggerAudio = event.getTriggerAudio();
+ result = result + "TriggerAudio: " + (triggerAudio == null ? "null" : triggerAudio.length);
+ result = result + "CaptureSession: " + event.getCaptureSession();
+ result += " )";
+ return result;
+ }
+
+ private void postMessage(String msg) {
+ showMessage(msg, Log.INFO, false);
+ }
+
+ private void postError(String msg) {
+ showMessage(msg, Log.ERROR, false);
+ }
+
+ private void postToast(String msg) {
+ showMessage(msg, Log.INFO, true);
+ }
+
+ private void postErrorToast(String msg) {
+ showMessage(msg, Log.ERROR, true);
+ }
+
+ /** Logs the message at the specified level, then forwards it to the activity if present. */
+ private synchronized void showMessage(String msg, int logLevel, boolean showToast) {
+ Log.println(logLevel, TAG, msg);
+ if (mUserActivity != null) {
+ mUserActivity.showMessage(msg, showToast);
+ }
+ }
+
+ private synchronized void setModelState(ModelInfo modelInfo, String state) {
+ modelInfo.state = state;
+ if (mUserActivity != null) {
+ mUserActivity.setModelState(modelInfo.modelUuid, modelInfo.state);
+ }
+ }
+}
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
index 1c95c25..8e5ed32 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
@@ -18,7 +18,6 @@
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.soundtrigger.SoundTrigger;
import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
import android.media.soundtrigger.SoundTriggerDetector;
import android.media.soundtrigger.SoundTriggerManager;
@@ -36,7 +35,7 @@
* Utility class for the managing sound trigger sound models.
*/
public class SoundTriggerUtil {
- private static final String TAG = "TestSoundTriggerUtil:Hotsound";
+ private static final String TAG = "SoundTriggerTestUtil";
private final ISoundTriggerService mSoundTriggerService;
private final SoundTriggerManager mSoundTriggerManager;
@@ -68,10 +67,6 @@
return true;
}
- public void addOrUpdateSoundModel(SoundTriggerManager.Model soundModel) {
- mSoundTriggerManager.updateModel(soundModel);
- }
-
/**
* Gets the sound model for the given keyphrase, null if none exists.
* If a sound model for a given keyphrase exists, and it needs to be updated,
@@ -91,7 +86,7 @@
}
if (model == null) {
- Log.w(TAG, "No models present for the gien keyphrase ID");
+ Log.w(TAG, "No models present for the given keyphrase ID");
return null;
} else {
return model;
@@ -109,18 +104,14 @@
try {
mSoundTriggerService.deleteSoundModel(new ParcelUuid(modelId));
} catch (RemoteException e) {
- Log.e(TAG, "RemoteException in updateSoundModel");
+ Log.e(TAG, "RemoteException in deleteSoundModel");
+ return false;
}
return true;
}
- public void deleteSoundModelUsingManager(UUID modelId) {
- mSoundTriggerManager.deleteModel(modelId);
- }
-
public SoundTriggerDetector createSoundTriggerDetector(UUID modelId,
SoundTriggerDetector.Callback callback) {
return mSoundTriggerManager.createSoundTriggerDetector(modelId, callback, null);
}
-
}
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java
deleted file mode 100644
index 5fd38e9..0000000
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.test.soundtrigger;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Random;
-import java.util.UUID;
-
-import android.app.Activity;
-import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
-import android.hardware.soundtrigger.SoundTrigger;
-import android.media.AudioFormat;
-import android.media.AudioManager;
-import android.media.MediaPlayer;
-import android.media.soundtrigger.SoundTriggerDetector;
-import android.media.soundtrigger.SoundTriggerManager;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.os.UserManager;
-import android.text.Editable;
-import android.text.method.ScrollingMovementMethod;
-import android.util.Log;
-import android.view.View;
-import android.widget.Button;
-import android.widget.RadioButton;
-import android.widget.RadioButton;
-import android.widget.RadioGroup;
-import android.widget.ScrollView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-public class TestSoundTriggerActivity extends Activity {
- private static final String TAG = "TestSoundTriggerActivity";
- private static final boolean DBG = false;
-
- private SoundTriggerUtil mSoundTriggerUtil;
- private Random mRandom;
-
- private Map<Integer, ModelInfo> mModelInfoMap;
- private Map<View, Integer> mModelIdMap;
-
- private TextView mDebugView = null;
- private int mSelectedModelId = -1;
- private ScrollView mScrollView = null;
- private Button mPlayTriggerButton = null;
- private PowerManager.WakeLock mScreenWakelock;
- private Handler mHandler;
- private RadioGroup mRadioGroup;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- if (DBG) Log.d(TAG, "onCreate");
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- mDebugView = (TextView) findViewById(R.id.console);
- mScrollView = (ScrollView) findViewById(R.id.scroller_id);
- mRadioGroup = (RadioGroup) findViewById(R.id.model_group_id);
- mPlayTriggerButton = (Button) findViewById(R.id.play_trigger_id);
- mDebugView.setText(mDebugView.getText(), TextView.BufferType.EDITABLE);
- mDebugView.setMovementMethod(new ScrollingMovementMethod());
- mSoundTriggerUtil = new SoundTriggerUtil(this);
- mRandom = new Random();
- mHandler = new Handler();
-
- mModelInfoMap = new HashMap();
- mModelIdMap = new HashMap();
-
- setVolumeControlStream(AudioManager.STREAM_MUSIC);
-
- // Load all the models in the data dir.
- for (File file : getFilesDir().listFiles()) {
- // Find meta-data in .properties files, ignore everything else.
- if (!file.getName().endsWith(".properties")) {
- continue;
- }
- try {
- Properties properties = new Properties();
- properties.load(new FileInputStream(file));
- createModelInfoAndWidget(properties);
- } catch (Exception e) {
- Log.e(TAG, "Failed to load properties file " + file.getName());
- }
- }
-
- // Create a few dummy models if we didn't load anything.
- if (mModelIdMap.isEmpty()) {
- Properties dummyModelProperties = new Properties();
- for (String name : new String[]{"One", "Two", "Three"}) {
- dummyModelProperties.setProperty("name", "Model " + name);
- createModelInfoAndWidget(dummyModelProperties);
- }
- }
- }
-
- private void createModelInfoAndWidget(Properties properties) {
- try {
- ModelInfo modelInfo = new ModelInfo();
-
- if (!properties.containsKey("name")) {
- throw new RuntimeException("must have a 'name' property");
- }
- modelInfo.name = properties.getProperty("name");
-
- if (properties.containsKey("modelUuid")) {
- modelInfo.modelUuid = UUID.fromString(properties.getProperty("modelUuid"));
- } else {
- modelInfo.modelUuid = UUID.randomUUID();
- }
-
- if (properties.containsKey("vendorUuid")) {
- modelInfo.vendorUuid = UUID.fromString(properties.getProperty("vendorUuid"));
- } else {
- modelInfo.vendorUuid = UUID.randomUUID();
- }
-
- if (properties.containsKey("triggerAudio")) {
- modelInfo.triggerAudioPlayer = MediaPlayer.create(this, Uri.parse(
- getFilesDir().getPath() + "/" + properties.getProperty("triggerAudio")));
- }
-
- if (properties.containsKey("dataFile")) {
- File modelDataFile = new File(
- getFilesDir().getPath() + "/" + properties.getProperty("dataFile"));
- modelInfo.modelData = new byte[(int) modelDataFile.length()];
- FileInputStream input = new FileInputStream(modelDataFile);
- input.read(modelInfo.modelData, 0, modelInfo.modelData.length);
- } else {
- modelInfo.modelData = new byte[1024];
- mRandom.nextBytes(modelInfo.modelData);
- }
-
- // TODO: Add property support for keyphrase models when they're exposed by the
- // service. Also things like how much audio they should record with the capture session
- // provided in the callback.
-
- // Add a widget into the radio group.
- RadioButton button = new RadioButton(this);
- mRadioGroup.addView(button);
- button.setText(modelInfo.name);
- button.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- onRadioButtonClicked(v);
- }
- });
-
- // Update our maps containing the button -> id and id -> modelInfo.
- int newModelId = mModelIdMap.size() + 1;
- mModelIdMap.put(button, newModelId);
- mModelInfoMap.put(newModelId, modelInfo);
-
- // If we don't have something selected, select this first thing.
- if (mSelectedModelId < 0) {
- button.setChecked(true);
- onRadioButtonClicked(button);
- }
- } catch (IOException e) {
- Log.e(TAG, "Error parsing properties for " + properties.getProperty("name"), e);
- }
- }
-
- private void postMessage(String msg) {
- Log.i(TAG, "Posted: " + msg);
- ((Editable) mDebugView.getText()).append(msg + "\n");
- if ((mDebugView.getMeasuredHeight() - mScrollView.getScrollY()) <=
- (mScrollView.getHeight() + mDebugView.getLineHeight())) {
- scrollToBottom();
- }
- }
-
- private void scrollToBottom() {
- mScrollView.post(new Runnable() {
- public void run() {
- mScrollView.smoothScrollTo(0, mDebugView.getBottom());
- }
- });
- }
-
- private synchronized UUID getSelectedUuid() {
- return mModelInfoMap.get(mSelectedModelId).modelUuid;
- }
-
- private synchronized void setDetector(SoundTriggerDetector detector) {
- mModelInfoMap.get(mSelectedModelId).detector = detector;
- }
-
- private synchronized SoundTriggerDetector getDetector() {
- return mModelInfoMap.get(mSelectedModelId).detector;
- }
-
- private void screenWakeup() {
- PowerManager pm = ((PowerManager)getSystemService(POWER_SERVICE));
- if (mScreenWakelock == null) {
- mScreenWakelock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "TAG");
- }
- mScreenWakelock.acquire();
- }
-
- private void screenRelease() {
- PowerManager pm = ((PowerManager)getSystemService(POWER_SERVICE));
- mScreenWakelock.release();
- }
-
- /** TODO: Should return the abstract sound model that can be then sent to the service. */
- private GenericSoundModel createNewSoundModel() {
- ModelInfo modelInfo = mModelInfoMap.get(mSelectedModelId);
- return new GenericSoundModel(modelInfo.modelUuid, modelInfo.vendorUuid,
- modelInfo.modelData);
- }
-
- /**
- * Called when the user clicks the enroll button.
- * Performs a fresh enrollment.
- */
- public void onEnrollButtonClicked(View v) {
- postMessage("Loading model: " + mSelectedModelId);
-
- GenericSoundModel model = createNewSoundModel();
-
- boolean status = mSoundTriggerUtil.addOrUpdateSoundModel(model);
- if (status) {
- Toast.makeText(
- this, "Successfully created sound trigger model UUID=" + model.uuid,
- Toast.LENGTH_SHORT).show();
- } else {
- Toast.makeText(this, "Failed to enroll!!!" + model.uuid, Toast.LENGTH_SHORT).show();
- }
-
- // Test the SoundManager API.
- }
-
- /**
- * Called when the user clicks the un-enroll button.
- * Clears the enrollment information for the user.
- */
- public void onUnEnrollButtonClicked(View v) {
- postMessage("Unloading model: " + mSelectedModelId);
- UUID modelUuid = getSelectedUuid();
- GenericSoundModel soundModel = mSoundTriggerUtil.getSoundModel(modelUuid);
- if (soundModel == null) {
- Toast.makeText(this, "Sound model not found!!!", Toast.LENGTH_SHORT).show();
- return;
- }
- boolean status = mSoundTriggerUtil.deleteSoundModel(modelUuid);
- if (status) {
- Toast.makeText(this, "Successfully deleted model UUID=" + soundModel.uuid,
- Toast.LENGTH_SHORT)
- .show();
- } else {
- Toast.makeText(this, "Failed to delete sound model!!!", Toast.LENGTH_SHORT).show();
- }
- }
-
- /**
- * Called when the user clicks the re-enroll button.
- * Uses the previously enrolled sound model and makes changes to it before pushing it back.
- */
- public void onReEnrollButtonClicked(View v) {
- postMessage("Re-loading model: " + mSelectedModelId);
- UUID modelUuid = getSelectedUuid();
- GenericSoundModel soundModel = mSoundTriggerUtil.getSoundModel(modelUuid);
- if (soundModel == null) {
- Toast.makeText(this, "Sound model not found!!!", Toast.LENGTH_SHORT).show();
- return;
- }
- GenericSoundModel updated = createNewSoundModel();
- boolean status = mSoundTriggerUtil.addOrUpdateSoundModel(updated);
- if (status) {
- Toast.makeText(this, "Successfully re-enrolled, model UUID=" + updated.uuid,
- Toast.LENGTH_SHORT)
- .show();
- } else {
- Toast.makeText(this, "Failed to re-enroll!!!", Toast.LENGTH_SHORT).show();
- }
- }
-
- public void onStartRecognitionButtonClicked(View v) {
- UUID modelUuid = getSelectedUuid();
- SoundTriggerDetector detector = getDetector();
- if (detector == null) {
- Log.i(TAG, "Created an instance of the SoundTriggerDetector for model #" +
- mSelectedModelId);
- postMessage("Created an instance of the SoundTriggerDetector for model #" +
- mSelectedModelId);
- detector = mSoundTriggerUtil.createSoundTriggerDetector(modelUuid,
- new DetectorCallback());
- setDetector(detector);
- }
- postMessage("Triggering start recognition for model: " + mSelectedModelId);
- if (!detector.startRecognition(
- SoundTriggerDetector.RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS)) {
- Log.e(TAG, "Fast failure attempting to start recognition.");
- postMessage("Fast failure attempting to start recognition:" + mSelectedModelId);
- }
- }
-
- public void onStopRecognitionButtonClicked(View v) {
- SoundTriggerDetector detector = getDetector();
- if (detector == null) {
- Log.e(TAG, "Stop called on null detector.");
- postMessage("Error: Stop called on null detector.");
- return;
- }
- postMessage("Triggering stop recognition for model: " + mSelectedModelId);
- if (!detector.stopRecognition()) {
- Log.e(TAG, "Fast failure attempting to stop recognition.");
- postMessage("Fast failure attempting to stop recognition: " + mSelectedModelId);
- }
- }
-
- public synchronized void onRadioButtonClicked(View view) {
- // Is the button now checked?
- boolean checked = ((RadioButton) view).isChecked();
- if (checked) {
- mSelectedModelId = mModelIdMap.get(view);
- ModelInfo modelInfo = mModelInfoMap.get(mSelectedModelId);
- postMessage("Selected " + modelInfo.name);
-
- // Set the play trigger button to be enabled only if we actually have some audio.
- mPlayTriggerButton.setEnabled(modelInfo.triggerAudioPlayer != null);
- }
- }
-
- public synchronized void onPlayTriggerButtonClicked(View v) {
- ModelInfo modelInfo = mModelInfoMap.get(mSelectedModelId);
- modelInfo.triggerAudioPlayer.start();
- postMessage("Playing trigger audio for " + modelInfo.name);
- }
-
- // Helper struct for holding information about a model.
- private static class ModelInfo {
- public String name;
- public UUID modelUuid;
- public UUID vendorUuid;
- public MediaPlayer triggerAudioPlayer;
- public SoundTriggerDetector detector;
- public byte modelData[];
- };
-
- // Implementation of SoundTriggerDetector.Callback.
- public class DetectorCallback extends SoundTriggerDetector.Callback {
- public void onAvailabilityChanged(int status) {
- postMessage("Availability changed to: " + status);
- }
-
- public void onDetected(SoundTriggerDetector.EventPayload event) {
- postMessage("onDetected(): " + eventPayloadToString(event));
- screenWakeup();
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- screenRelease();
- }
- }, 1000L);
- }
-
- public void onError() {
- postMessage("onError()");
- }
-
- public void onRecognitionPaused() {
- postMessage("onRecognitionPaused()");
- }
-
- public void onRecognitionResumed() {
- postMessage("onRecognitionResumed()");
- }
- }
-
- private String eventPayloadToString(SoundTriggerDetector.EventPayload event) {
- String result = "EventPayload(";
- AudioFormat format = event.getCaptureAudioFormat();
- result = result + "AudioFormat: " + ((format == null) ? "null" : format.toString());
- byte[] triggerAudio = event.getTriggerAudio();
- result = result + "TriggerAudio: " + (triggerAudio == null ? "null" : triggerAudio.length);
- result = result + "CaptureSession: " + event.getCaptureSession();
- result += " )";
- return result;
- }
-}
diff --git a/tests/UiBench/src/com/android/test/uibench/MainActivity.java b/tests/UiBench/src/com/android/test/uibench/MainActivity.java
index 2111274..79837b6 100644
--- a/tests/UiBench/src/com/android/test/uibench/MainActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/MainActivity.java
@@ -38,6 +38,23 @@
private static final String EXTRA_PATH = "activity_path";
private static final String CATEGORY_HWUI_TEST = "com.android.test.uibench.TEST";
+ public static class TestListFragment extends ListFragment {
+ @Override
+ @SuppressWarnings("unchecked")
+ public void onListItemClick(ListView l, View v, int position, long id) {
+ Map<String, Object> map = (Map<String, Object>)l.getItemAtPosition(position);
+
+ Intent intent = (Intent) map.get("intent");
+ startActivity(intent);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ getListView().setTextFilterEnabled(true);
+ }
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -54,22 +71,7 @@
FragmentManager fm = getSupportFragmentManager();
if (fm.findFragmentById(android.R.id.content) == null) {
- ListFragment listFragment = new ListFragment() {
- @Override
- @SuppressWarnings("unchecked")
- public void onListItemClick(ListView l, View v, int position, long id) {
- Map<String, Object> map = (Map<String, Object>)l.getItemAtPosition(position);
-
- Intent intent = (Intent) map.get("intent");
- startActivity(intent);
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- getListView().setTextFilterEnabled(true);
- }
- };
+ ListFragment listFragment = new TestListFragment();
listFragment.setListAdapter(new SimpleAdapter(this, getData(path),
android.R.layout.simple_list_item_1, new String[] { "title" },
new int[] { android.R.id.text1 }));
diff --git a/tests/UiBench/src/com/android/test/uibench/ShadowGridActivity.java b/tests/UiBench/src/com/android/test/uibench/ShadowGridActivity.java
index d32f071..88847ee 100644
--- a/tests/UiBench/src/com/android/test/uibench/ShadowGridActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/ShadowGridActivity.java
@@ -23,19 +23,22 @@
import android.widget.ArrayAdapter;
public class ShadowGridActivity extends AppCompatActivity {
+ public static class NoDividerListFragment extends ListFragment {
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ getListView().setDivider(null);
+ }
+ };
+
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getSupportFragmentManager();
if (fm.findFragmentById(android.R.id.content) == null) {
- ListFragment listFragment = new ListFragment() {
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- getListView().setDivider(null);
- }
- };
+ ListFragment listFragment = new NoDividerListFragment();
listFragment.setListAdapter(new ArrayAdapter<>(this,
R.layout.card_row, R.id.card_text, TextUtils.buildSimpleStringList()));
diff --git a/tests/WallpaperTest/res/values/strings.xml b/tests/WallpaperTest/res/values/strings.xml
index fd21259..80a3d49 100644
--- a/tests/WallpaperTest/res/values/strings.xml
+++ b/tests/WallpaperTest/res/values/strings.xml
@@ -24,6 +24,9 @@
Test wallpaper for use with the wallpaper test app.
</string>
+ <string name="test_wallpaper_context_uri">https://www.google.com/maps/@37.8092876,-122.408986,1391m/data=!3m1!1e3</string>
+ <string name="test_wallpaper_context_description">Explore</string>
+
<string name="dimens">Dimens: </string>
<string name="width">Width: </string>
<string name="height">Height: </string>
diff --git a/tests/WallpaperTest/res/xml/test_wallpaper.xml b/tests/WallpaperTest/res/xml/test_wallpaper.xml
index 9f7d714b..ba22478 100644
--- a/tests/WallpaperTest/res/xml/test_wallpaper.xml
+++ b/tests/WallpaperTest/res/xml/test_wallpaper.xml
@@ -23,4 +23,7 @@
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android"
android:author="@string/test_wallpaper_author"
android:description="@string/test_wallpaper_desc"
- android:thumbnail="@drawable/test_wallpaper_thumb" />
+ android:thumbnail="@drawable/test_wallpaper_thumb"
+ android:showMetadataInPreview="true"
+ android:contextUri="@string/test_wallpaper_context_uri"
+ android:contextDescription="@string/test_wallpaper_context_description"/>
diff --git a/tests/WallpaperTest/src/com/example/wallpapertest/TestWallpaper.java b/tests/WallpaperTest/src/com/example/wallpapertest/TestWallpaper.java
index 95db6d1..ab36c22 100644
--- a/tests/WallpaperTest/src/com/example/wallpapertest/TestWallpaper.java
+++ b/tests/WallpaperTest/src/com/example/wallpapertest/TestWallpaper.java
@@ -144,6 +144,14 @@
@Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
+
+ // Simulate some slowness, so we can test the loading process in the live wallpaper
+ // picker.
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
}
@Override
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index a726a15..063dd86 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -93,7 +93,7 @@
try {
mWm.addAppToken(0, null, 0, 0, 0, false, false, 0, 0, false, false, null,
- Configuration.EMPTY, 0, false, false, 0);
+ Configuration.EMPTY, 0, false, false, 0, -1);
fail("IWindowManager.addAppToken did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
diff --git a/tests/utils/testutils/Android.mk b/tests/utils/testutils/Android.mk
new file mode 100644
index 0000000..d53167f
--- /dev/null
+++ b/tests/utils/testutils/Android.mk
@@ -0,0 +1,30 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := frameworks-base-testutils
+LOCAL_MODULE_TAG := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under,java)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ mockito-target
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/utils/testutils/java/android/app/test/MockAnswerUtil.java b/tests/utils/testutils/java/android/app/test/MockAnswerUtil.java
new file mode 100644
index 0000000..746c77d
--- /dev/null
+++ b/tests/utils/testutils/java/android/app/test/MockAnswerUtil.java
@@ -0,0 +1,62 @@
+/*
+ * 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.app.test;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+/**
+ * Utilities for creating Answers for mock objects
+ */
+public class MockAnswerUtil {
+
+ /**
+ * Answer that calls the method in the Answer called "answer" that matches the type signature of
+ * the method being answered. An error will be thrown at runtime if the signature does not match
+ * exactly.
+ */
+ public static class AnswerWithArguments implements Answer<Object> {
+ @Override
+ public final Object answer(InvocationOnMock invocation) throws Throwable {
+ Method method = invocation.getMethod();
+ try {
+ Method implementation = getClass().getMethod("answer", method.getParameterTypes());
+ if (!implementation.getReturnType().equals(method.getReturnType())) {
+ throw new RuntimeException("Found answer method does not have expected return "
+ + "type. Expected: " + method.getReturnType() + ", got "
+ + implementation.getReturnType());
+ }
+ Object[] args = invocation.getArguments();
+ try {
+ return implementation.invoke(this, args);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Error invoking answer method", e);
+ } catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("Could not find answer method with the expected args "
+ + Arrays.toString(method.getParameterTypes()), e);
+ }
+ }
+ }
+
+}
diff --git a/tests/utils/testutils/java/android/app/test/TestAlarmManager.java b/tests/utils/testutils/java/android/app/test/TestAlarmManager.java
new file mode 100644
index 0000000..e90ea1e
--- /dev/null
+++ b/tests/utils/testutils/java/android/app/test/TestAlarmManager.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.test;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+
+import android.app.AlarmManager;
+import android.app.test.MockAnswerUtil.AnswerWithArguments;
+import android.os.Handler;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Creates an AlarmManager whose alarm dispatch can be controlled
+ * Currently only supports alarm listeners
+ *
+ * Alarm listeners will be dispatched to the handler provided or will
+ * be dispatched immediately if they would have been sent to the main
+ * looper (handler was null).
+ */
+public class TestAlarmManager {
+ private final AlarmManager mAlarmManager;
+ private final List<PendingAlarm> mPendingAlarms;
+
+ public TestAlarmManager() throws Exception {
+ mPendingAlarms = new ArrayList<>();
+
+ mAlarmManager = mock(AlarmManager.class);
+ doAnswer(new SetListenerAnswer()).when(mAlarmManager).set(anyInt(), anyLong(), anyString(),
+ any(AlarmManager.OnAlarmListener.class), any(Handler.class));
+ doAnswer(new SetListenerAnswer()).when(mAlarmManager).setExact(anyInt(), anyLong(),
+ anyString(), any(AlarmManager.OnAlarmListener.class), any(Handler.class));
+ doAnswer(new CancelListenerAnswer())
+ .when(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class));
+ }
+
+ public AlarmManager getAlarmManager() {
+ return mAlarmManager;
+ }
+
+ /**
+ * Dispatch a pending alarm with the given tag
+ * @return if any alarm was dispatched
+ */
+ public boolean dispatch(String tag) {
+ for (int i = 0; i < mPendingAlarms.size(); ++i) {
+ PendingAlarm alarm = mPendingAlarms.get(i);
+ if (Objects.equals(tag, alarm.getTag())) {
+ mPendingAlarms.remove(i);
+ alarm.dispatch();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return if an alarm with the given tag is pending
+ */
+ public boolean isPending(String tag) {
+ for (int i = 0; i < mPendingAlarms.size(); ++i) {
+ PendingAlarm alarm = mPendingAlarms.get(i);
+ if (Objects.equals(tag, alarm.getTag())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return trigger time of an pending alarm with the given tag
+ * -1 if no pending alarm with the given tag
+ */
+ public long getTriggerTimeMillis(String tag) {
+ for (int i = 0; i < mPendingAlarms.size(); ++i) {
+ PendingAlarm alarm = mPendingAlarms.get(i);
+ if (Objects.equals(tag, alarm.getTag())) {
+ return alarm.getTriggerTimeMillis();
+ }
+ }
+ return -1;
+ }
+
+ private static class PendingAlarm {
+ private final int mType;
+ private final long mTriggerAtMillis;
+ private final String mTag;
+ private final Runnable mCallback;
+
+ public PendingAlarm(int type, long triggerAtMillis, String tag, Runnable callback) {
+ mType = type;
+ mTriggerAtMillis = triggerAtMillis;
+ mTag = tag;
+ mCallback = callback;
+ }
+
+ public void dispatch() {
+ if (mCallback != null) {
+ mCallback.run();
+ }
+ }
+
+ public Runnable getCallback() {
+ return mCallback;
+ }
+
+ public String getTag() {
+ return mTag;
+ }
+
+ public long getTriggerTimeMillis() {
+ return mTriggerAtMillis;
+ }
+ }
+
+ private class SetListenerAnswer extends AnswerWithArguments {
+ public void answer(int type, long triggerAtMillis, String tag,
+ AlarmManager.OnAlarmListener listener, Handler handler) {
+ mPendingAlarms.add(new PendingAlarm(type, triggerAtMillis, tag,
+ new AlarmListenerRunnable(listener, handler)));
+ }
+ }
+
+ private class CancelListenerAnswer extends AnswerWithArguments {
+ public void answer(AlarmManager.OnAlarmListener listener) {
+ Iterator<PendingAlarm> alarmItr = mPendingAlarms.iterator();
+ while (alarmItr.hasNext()) {
+ PendingAlarm alarm = alarmItr.next();
+ if (alarm.getCallback() instanceof AlarmListenerRunnable) {
+ AlarmListenerRunnable alarmCallback =
+ (AlarmListenerRunnable) alarm.getCallback();
+ if (alarmCallback.getListener() == listener) {
+ alarmItr.remove();
+ }
+ }
+ }
+ }
+ }
+
+ private static class AlarmListenerRunnable implements Runnable {
+ private final AlarmManager.OnAlarmListener mListener;
+ private final Handler mHandler;
+ public AlarmListenerRunnable(AlarmManager.OnAlarmListener listener, Handler handler) {
+ mListener = listener;
+ mHandler = handler;
+ }
+
+ public AlarmManager.OnAlarmListener getListener() {
+ return mListener;
+ }
+
+ @Override
+ public void run() {
+ if (mHandler != null) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onAlarm();
+ }
+ });
+ } else { // normally gets dispatched in main looper
+ mListener.onAlarm();
+ }
+ }
+ }
+}
diff --git a/tests/utils/testutils/java/android/os/test/TestLooper.java b/tests/utils/testutils/java/android/os/test/TestLooper.java
new file mode 100644
index 0000000..e8ceb4a
--- /dev/null
+++ b/tests/utils/testutils/java/android/os/test/TestLooper.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2015 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.test;
+
+import static org.junit.Assert.assertTrue;
+
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Creates a looper whose message queue can be manipulated
+ * This allows testing code that uses a looper to dispatch messages in a deterministic manner
+ * Creating a TestLooper will also install it as the looper for the current thread
+ */
+public class TestLooper {
+ protected final Looper mLooper;
+
+ private static final Constructor<Looper> LOOPER_CONSTRUCTOR;
+ private static final Field THREAD_LOCAL_LOOPER_FIELD;
+ private static final Field MESSAGE_QUEUE_MESSAGES_FIELD;
+ private static final Field MESSAGE_NEXT_FIELD;
+ private static final Field MESSAGE_WHEN_FIELD;
+ private static final Method MESSAGE_MARK_IN_USE_METHOD;
+ private static final String TAG = "TestLooper";
+
+ private AutoDispatchThread mAutoDispatchThread;
+
+ static {
+ try {
+ LOOPER_CONSTRUCTOR = Looper.class.getDeclaredConstructor(Boolean.TYPE);
+ LOOPER_CONSTRUCTOR.setAccessible(true);
+ THREAD_LOCAL_LOOPER_FIELD = Looper.class.getDeclaredField("sThreadLocal");
+ THREAD_LOCAL_LOOPER_FIELD.setAccessible(true);
+ MESSAGE_QUEUE_MESSAGES_FIELD = MessageQueue.class.getDeclaredField("mMessages");
+ MESSAGE_QUEUE_MESSAGES_FIELD.setAccessible(true);
+ MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
+ MESSAGE_NEXT_FIELD.setAccessible(true);
+ MESSAGE_WHEN_FIELD = Message.class.getDeclaredField("when");
+ MESSAGE_WHEN_FIELD.setAccessible(true);
+ MESSAGE_MARK_IN_USE_METHOD = Message.class.getDeclaredMethod("markInUse");
+ MESSAGE_MARK_IN_USE_METHOD.setAccessible(true);
+ } catch (NoSuchFieldException | NoSuchMethodException e) {
+ throw new RuntimeException("Failed to initialize TestLooper", e);
+ }
+ }
+
+
+ public TestLooper() {
+ try {
+ mLooper = LOOPER_CONSTRUCTOR.newInstance(false);
+
+ ThreadLocal<Looper> threadLocalLooper = (ThreadLocal<Looper>) THREAD_LOCAL_LOOPER_FIELD
+ .get(null);
+ threadLocalLooper.set(mLooper);
+ } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
+ throw new RuntimeException("Reflection error constructing or accessing looper", e);
+ }
+ }
+
+ public Looper getLooper() {
+ return mLooper;
+ }
+
+ private Message getMessageLinkedList() {
+ try {
+ MessageQueue queue = mLooper.getQueue();
+ return (Message) MESSAGE_QUEUE_MESSAGES_FIELD.get(queue);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Access failed in TestLooper: get - MessageQueue.mMessages",
+ e);
+ }
+ }
+
+ public void moveTimeForward(long milliSeconds) {
+ try {
+ Message msg = getMessageLinkedList();
+ while (msg != null) {
+ long updatedWhen = msg.getWhen() - milliSeconds;
+ if (updatedWhen < 0) {
+ updatedWhen = 0;
+ }
+ MESSAGE_WHEN_FIELD.set(msg, updatedWhen);
+ msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
+ }
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Access failed in TestLooper: set - Message.when", e);
+ }
+ }
+
+ private Message messageQueueNext() {
+ try {
+ long now = SystemClock.uptimeMillis();
+
+ Message prevMsg = null;
+ Message msg = getMessageLinkedList();
+ if (msg != null && msg.getTarget() == null) {
+ // Stalled by a barrier. Find the next asynchronous message in
+ // the queue.
+ do {
+ prevMsg = msg;
+ msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
+ } while (msg != null && !msg.isAsynchronous());
+ }
+ if (msg != null) {
+ if (now >= msg.getWhen()) {
+ // Got a message.
+ if (prevMsg != null) {
+ MESSAGE_NEXT_FIELD.set(prevMsg, MESSAGE_NEXT_FIELD.get(msg));
+ } else {
+ MESSAGE_QUEUE_MESSAGES_FIELD.set(mLooper.getQueue(),
+ MESSAGE_NEXT_FIELD.get(msg));
+ }
+ MESSAGE_NEXT_FIELD.set(msg, null);
+ MESSAGE_MARK_IN_USE_METHOD.invoke(msg);
+ return msg;
+ }
+ }
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new RuntimeException("Access failed in TestLooper", e);
+ }
+
+ return null;
+ }
+
+ /**
+ * @return true if there are pending messages in the message queue
+ */
+ public synchronized boolean isIdle() {
+ Message messageList = getMessageLinkedList();
+
+ return messageList != null && SystemClock.uptimeMillis() >= messageList.getWhen();
+ }
+
+ /**
+ * @return the next message in the Looper's message queue or null if there is none
+ */
+ public synchronized Message nextMessage() {
+ if (isIdle()) {
+ return messageQueueNext();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Dispatch the next message in the queue
+ * Asserts that there is a message in the queue
+ */
+ public synchronized void dispatchNext() {
+ assertTrue(isIdle());
+ Message msg = messageQueueNext();
+ if (msg == null) {
+ return;
+ }
+ msg.getTarget().dispatchMessage(msg);
+ }
+
+ /**
+ * Dispatch all messages currently in the queue
+ * Will not fail if there are no messages pending
+ * @return the number of messages dispatched
+ */
+ public synchronized int dispatchAll() {
+ int count = 0;
+ while (isIdle()) {
+ dispatchNext();
+ ++count;
+ }
+ return count;
+ }
+
+ /**
+ * Thread used to dispatch messages when the main thread is blocked waiting for a response.
+ */
+ private class AutoDispatchThread extends Thread {
+ private static final int MAX_LOOPS = 100;
+ private static final int LOOP_SLEEP_TIME_MS = 10;
+
+ private RuntimeException mAutoDispatchException = null;
+
+ /**
+ * Run method for the auto dispatch thread.
+ * The thread loops a maximum of MAX_LOOPS times with a 10ms sleep between loops.
+ * The thread continues looping and attempting to dispatch all messages until at
+ * least one message has been dispatched.
+ */
+ @Override
+ public void run() {
+ int dispatchCount = 0;
+ for (int i = 0; i < MAX_LOOPS; i++) {
+ try {
+ dispatchCount = dispatchAll();
+ } catch (RuntimeException e) {
+ mAutoDispatchException = e;
+ }
+ Log.d(TAG, "dispatched " + dispatchCount + " messages");
+ if (dispatchCount > 0) {
+ return;
+ }
+ try {
+ Thread.sleep(LOOP_SLEEP_TIME_MS);
+ } catch (InterruptedException e) {
+ mAutoDispatchException = new IllegalStateException(
+ "stopAutoDispatch called before any messages were dispatched.");
+ return;
+ }
+ }
+ Log.e(TAG, "AutoDispatchThread did not dispatch any messages.");
+ mAutoDispatchException = new IllegalStateException(
+ "TestLooper did not dispatch any messages before exiting.");
+ }
+
+ /**
+ * Method allowing the TestLooper to pass any exceptions thrown by the thread to be passed
+ * to the main thread.
+ *
+ * @return RuntimeException Exception created by stopping without dispatching a message
+ */
+ public RuntimeException getException() {
+ return mAutoDispatchException;
+ }
+ }
+
+ /**
+ * Create and start a new AutoDispatchThread if one is not already running.
+ */
+ public void startAutoDispatch() {
+ if (mAutoDispatchThread != null) {
+ throw new IllegalStateException(
+ "startAutoDispatch called with the AutoDispatchThread already running.");
+ }
+ mAutoDispatchThread = new AutoDispatchThread();
+ mAutoDispatchThread.start();
+ }
+
+ /**
+ * If an AutoDispatchThread is currently running, stop and clean up.
+ */
+ public void stopAutoDispatch() {
+ if (mAutoDispatchThread != null) {
+ if (mAutoDispatchThread.isAlive()) {
+ mAutoDispatchThread.interrupt();
+ }
+ try {
+ mAutoDispatchThread.join();
+ } catch (InterruptedException e) {
+ // Catch exception from join.
+ }
+
+ RuntimeException e = mAutoDispatchThread.getException();
+ mAutoDispatchThread = null;
+ if (e != null) {
+ throw e;
+ }
+ } else {
+ // stopAutoDispatch was called when startAutoDispatch has not created a new thread.
+ throw new IllegalStateException(
+ "stopAutoDispatch called without startAutoDispatch.");
+ }
+ }
+}
diff --git a/tests/utils/testutils/java/android/os/test/TestLooperTest.java b/tests/utils/testutils/java/android/os/test/TestLooperTest.java
new file mode 100644
index 0000000..40d83b5
--- /dev/null
+++ b/tests/utils/testutils/java/android/os/test/TestLooperTest.java
@@ -0,0 +1,371 @@
+/*
+ * 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.os.test;
+
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ErrorCollector;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Test TestLooperAbstractTime which provides control over "time". Note that
+ * real-time is being used as well. Therefore small time increments are NOT
+ * reliable. All tests are in "K" units (i.e. *1000).
+ */
+
+@SmallTest
+public class TestLooperTest {
+ private TestLooper mTestLooper;
+ private Handler mHandler;
+ private Handler mHandlerSpy;
+
+ @Rule
+ public ErrorCollector collector = new ErrorCollector();
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mTestLooper = new TestLooper();
+ mHandler = new Handler(mTestLooper.getLooper());
+ mHandlerSpy = spy(mHandler);
+ }
+
+ /**
+ * Basic test with no time stamps: dispatch 4 messages, check that all 4
+ * delivered (in correct order).
+ */
+ @Test
+ public void testNoTimeMovement() {
+ final int messageA = 1;
+ final int messageB = 2;
+ final int messageC = 3;
+
+ InOrder inOrder = inOrder(mHandlerSpy);
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+
+ mHandlerSpy.sendMessage(mHandler.obtainMessage(messageA));
+ mHandlerSpy.sendMessage(mHandler.obtainMessage(messageA));
+ mHandlerSpy.sendMessage(mHandler.obtainMessage(messageB));
+ mHandlerSpy.sendMessage(mHandler.obtainMessage(messageC));
+ mTestLooper.dispatchAll();
+
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("1: messageA", messageA, equalTo(messageCaptor.getValue().what));
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("2: messageA", messageA, equalTo(messageCaptor.getValue().what));
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("3: messageB", messageB, equalTo(messageCaptor.getValue().what));
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("4: messageC", messageC, equalTo(messageCaptor.getValue().what));
+
+ inOrder.verify(mHandlerSpy, never()).handleMessage(any(Message.class));
+ }
+
+ /**
+ * Test message sequence: A, B, C@5K, A@10K. Don't move time.
+ * <p>
+ * Expected: only get A, B
+ */
+ @Test
+ public void testDelayedDispatchNoTimeMove() {
+ final int messageA = 1;
+ final int messageB = 2;
+ final int messageC = 3;
+
+ InOrder inOrder = inOrder(mHandlerSpy);
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+
+ mHandlerSpy.sendMessage(mHandler.obtainMessage(messageA));
+ mHandlerSpy.sendMessage(mHandler.obtainMessage(messageB));
+ mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageC), 5000);
+ mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageA), 10000);
+ mTestLooper.dispatchAll();
+
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("1: messageA", messageA, equalTo(messageCaptor.getValue().what));
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("2: messageB", messageB, equalTo(messageCaptor.getValue().what));
+
+ inOrder.verify(mHandlerSpy, never()).handleMessage(any(Message.class));
+ }
+
+ /**
+ * Test message sequence: A, B, C@5K, A@10K, Advance time by 5K.
+ * <p>
+ * Expected: only get A, B, C
+ */
+ @Test
+ public void testDelayedDispatchAdvanceTimeOnce() {
+ final int messageA = 1;
+ final int messageB = 2;
+ final int messageC = 3;
+
+ InOrder inOrder = inOrder(mHandlerSpy);
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+
+ mHandlerSpy.sendMessage(mHandler.obtainMessage(messageA));
+ mHandlerSpy.sendMessage(mHandler.obtainMessage(messageB));
+ mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageC), 5000);
+ mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageA), 10000);
+ mTestLooper.moveTimeForward(5000);
+ mTestLooper.dispatchAll();
+
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("1: messageA", messageA, equalTo(messageCaptor.getValue().what));
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("2: messageB", messageB, equalTo(messageCaptor.getValue().what));
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("3: messageC", messageC, equalTo(messageCaptor.getValue().what));
+
+ inOrder.verify(mHandlerSpy, never()).handleMessage(any(Message.class));
+ }
+
+ /**
+ * Test message sequence: A, B, C@5K, Advance time by 4K, A@1K, B@2K Advance
+ * time by 1K.
+ * <p>
+ * Expected: get A, B, C, A
+ */
+ @Test
+ public void testDelayedDispatchAdvanceTimeTwice() {
+ final int messageA = 1;
+ final int messageB = 2;
+ final int messageC = 3;
+
+ InOrder inOrder = inOrder(mHandlerSpy);
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+
+ mHandlerSpy.sendMessage(mHandler.obtainMessage(messageA));
+ mHandlerSpy.sendMessage(mHandler.obtainMessage(messageB));
+ mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageC), 5000);
+ mTestLooper.moveTimeForward(4000);
+ mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageA), 1000);
+ mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageB), 2000);
+ mTestLooper.moveTimeForward(1000);
+ mTestLooper.dispatchAll();
+
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("1: messageA", messageA, equalTo(messageCaptor.getValue().what));
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("2: messageB", messageB, equalTo(messageCaptor.getValue().what));
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("3: messageC", messageC, equalTo(messageCaptor.getValue().what));
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("4: messageA", messageA, equalTo(messageCaptor.getValue().what));
+
+ inOrder.verify(mHandlerSpy, never()).handleMessage(any(Message.class));
+ }
+
+ /**
+ * Test message sequence: A, B, C@5K, Advance time by 4K, A@5K, B@2K Advance
+ * time by 3K.
+ * <p>
+ * Expected: get A, B, C, B
+ */
+ @Test
+ public void testDelayedDispatchReverseOrder() {
+ final int messageA = 1;
+ final int messageB = 2;
+ final int messageC = 3;
+
+ InOrder inOrder = inOrder(mHandlerSpy);
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+
+ mHandlerSpy.sendMessage(mHandler.obtainMessage(messageA));
+ mHandlerSpy.sendMessage(mHandler.obtainMessage(messageB));
+ mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageC), 5000);
+ mTestLooper.moveTimeForward(4000);
+ mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageA), 5000);
+ mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageB), 2000);
+ mTestLooper.moveTimeForward(3000);
+ mTestLooper.dispatchAll();
+
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("1: messageA", messageA, equalTo(messageCaptor.getValue().what));
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("2: messageB", messageB, equalTo(messageCaptor.getValue().what));
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("3: messageC", messageC, equalTo(messageCaptor.getValue().what));
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("4: messageB", messageB, equalTo(messageCaptor.getValue().what));
+
+ inOrder.verify(mHandlerSpy, never()).handleMessage(any(Message.class));
+ }
+
+ /**
+ * Test message sequence: A, B, C@5K, Advance time by 4K, dispatch all,
+ * A@5K, B@2K Advance time by 3K, dispatch all.
+ * <p>
+ * Expected: get A, B after first dispatch; then C, B after second dispatch
+ */
+ @Test
+ public void testDelayedDispatchAllMultipleTimes() {
+ final int messageA = 1;
+ final int messageB = 2;
+ final int messageC = 3;
+
+ InOrder inOrder = inOrder(mHandlerSpy);
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+
+ mHandlerSpy.sendMessage(mHandler.obtainMessage(messageA));
+ mHandlerSpy.sendMessage(mHandler.obtainMessage(messageB));
+ mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageC), 5000);
+ mTestLooper.moveTimeForward(4000);
+ mTestLooper.dispatchAll();
+
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("1: messageA", messageA, equalTo(messageCaptor.getValue().what));
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("2: messageB", messageB, equalTo(messageCaptor.getValue().what));
+
+ mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageA), 5000);
+ mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageB), 2000);
+ mTestLooper.moveTimeForward(3000);
+ mTestLooper.dispatchAll();
+
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("3: messageC", messageC, equalTo(messageCaptor.getValue().what));
+ inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ collector.checkThat("4: messageB", messageB, equalTo(messageCaptor.getValue().what));
+
+ inOrder.verify(mHandlerSpy, never()).handleMessage(any(Message.class));
+ }
+
+ /**
+ * Test AutoDispatch for a single message.
+ * This test would ideally use the Channel sendMessageSynchronously. At this time, the setup to
+ * get a working test channel is cumbersome. Until this is fixed, we substitute with a
+ * sendMessage followed by a blocking call. The main test thread blocks until the test handler
+ * receives the test message (messageA) and sets a boolean true. Once the boolean is true, the
+ * main thread will exit the busy wait loop, stop autoDispatch and check the assert.
+ *
+ * Enable AutoDispatch, add message, block on message being handled and stop AutoDispatch.
+ * <p>
+ * Expected: handleMessage is called for messageA and stopAutoDispatch is called.
+ */
+ @Test
+ public void testAutoDispatchWithSingleMessage() {
+ final int mLoopSleepTimeMs = 5;
+
+ final int messageA = 1;
+
+ TestLooper mockLooper = new TestLooper();
+ class TestHandler extends Handler {
+ public volatile boolean handledMessage = false;
+ TestHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == messageA) {
+ handledMessage = true;
+ }
+ }
+ }
+
+ TestHandler testHandler = new TestHandler(mockLooper.getLooper());
+ mockLooper.startAutoDispatch();
+ testHandler.sendMessage(testHandler.obtainMessage(messageA));
+ while (!testHandler.handledMessage) {
+ // Block until message is handled
+ try {
+ Thread.sleep(mLoopSleepTimeMs);
+ } catch (InterruptedException e) {
+ // Interrupted while sleeping.
+ }
+ }
+ mockLooper.stopAutoDispatch();
+ assertTrue("TestHandler should have received messageA", testHandler.handledMessage);
+ }
+
+ /**
+ * Test starting AutoDispatch while already running throws IllegalStateException
+ * Enable AutoDispatch two times in a row.
+ * <p>
+ * Expected: catch IllegalStateException on second call.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testRepeatedStartAutoDispatchThrowsException() {
+ mTestLooper.startAutoDispatch();
+ mTestLooper.startAutoDispatch();
+ }
+
+ /**
+ * Test stopping AutoDispatch without previously starting throws IllegalStateException
+ * Stop AutoDispatch
+ * <p>
+ * Expected: catch IllegalStateException on second call.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testStopAutoDispatchWithoutStartThrowsException() {
+ mTestLooper.stopAutoDispatch();
+ }
+
+ /**
+ * Test AutoDispatch exits and does not dispatch a later message.
+ * Start and stop AutoDispatch then add a message.
+ * <p>
+ * Expected: After AutoDispatch is stopped, dispatchAll will return 1.
+ */
+ @Test
+ public void testAutoDispatchStopsCleanlyWithoutDispatchingAMessage() {
+ final int messageA = 1;
+
+ InOrder inOrder = inOrder(mHandlerSpy);
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+
+ mTestLooper.startAutoDispatch();
+ try {
+ mTestLooper.stopAutoDispatch();
+ } catch (IllegalStateException e) {
+ // Stopping without a dispatch will throw an exception.
+ }
+
+ mHandlerSpy.sendMessage(mHandler.obtainMessage(messageA));
+ assertEquals("One message should be dispatched", 1, mTestLooper.dispatchAll());
+ }
+
+ /**
+ * Test AutoDispatch throws an exception when no messages are dispatched.
+ * Start and stop AutoDispatch
+ * <p>
+ * Expected: Exception is thrown with the stopAutoDispatch call.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testAutoDispatchThrowsExceptionWhenNoMessagesDispatched() {
+ mTestLooper.startAutoDispatch();
+ mTestLooper.stopAutoDispatch();
+ }
+}
diff --git a/tests/utils/testutils/java/com/android/internal/util/test/BidirectionalAsyncChannel.java b/tests/utils/testutils/java/com/android/internal/util/test/BidirectionalAsyncChannel.java
new file mode 100644
index 0000000..25cd5b9
--- /dev/null
+++ b/tests/utils/testutils/java/com/android/internal/util/test/BidirectionalAsyncChannel.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.util.test;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.util.Log;
+
+import com.android.internal.util.AsyncChannel;
+
+
+/**
+ * Provides an AsyncChannel interface that implements the connection initiating half of a
+ * bidirectional channel as described in {@link com.android.internal.util.AsyncChannel}.
+ */
+public class BidirectionalAsyncChannel {
+ private static final String TAG = "BidirectionalAsyncChannel";
+
+ private AsyncChannel mChannel;
+ public enum ChannelState { DISCONNECTED, HALF_CONNECTED, CONNECTED, FAILURE };
+ private ChannelState mState = ChannelState.DISCONNECTED;
+
+ public void assertConnected() {
+ assertEquals("AsyncChannel was not fully connected", ChannelState.CONNECTED, mState);
+ }
+
+ public void connect(final Looper looper, final Messenger messenger,
+ final Handler incomingMessageHandler) {
+ assertEquals("AsyncChannel must be disconnected to connect",
+ ChannelState.DISCONNECTED, mState);
+ mChannel = new AsyncChannel();
+ Handler rawMessageHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+ if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+ Log.d(TAG, "Successfully half connected " + this);
+ mChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+ mState = ChannelState.HALF_CONNECTED;
+ } else {
+ Log.d(TAG, "Failed to connect channel " + this);
+ mState = ChannelState.FAILURE;
+ mChannel = null;
+ }
+ break;
+ case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
+ mState = ChannelState.CONNECTED;
+ Log.d(TAG, "Channel fully connected" + this);
+ break;
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+ mState = ChannelState.DISCONNECTED;
+ mChannel = null;
+ Log.d(TAG, "Channel disconnected" + this);
+ break;
+ default:
+ incomingMessageHandler.handleMessage(msg);
+ break;
+ }
+ }
+ };
+ mChannel.connect(null, rawMessageHandler, messenger);
+ }
+
+ public void disconnect() {
+ assertEquals("AsyncChannel must be connected to disconnect",
+ ChannelState.CONNECTED, mState);
+ mChannel.sendMessage(AsyncChannel.CMD_CHANNEL_DISCONNECT);
+ mState = ChannelState.DISCONNECTED;
+ mChannel = null;
+ }
+
+ public void sendMessage(Message msg) {
+ assertEquals("AsyncChannel must be connected to send messages",
+ ChannelState.CONNECTED, mState);
+ mChannel.sendMessage(msg);
+ }
+}
diff --git a/tests/utils/testutils/java/com/android/internal/util/test/BidirectionalAsyncChannelServer.java b/tests/utils/testutils/java/com/android/internal/util/test/BidirectionalAsyncChannelServer.java
new file mode 100644
index 0000000..49c8332
--- /dev/null
+++ b/tests/utils/testutils/java/com/android/internal/util/test/BidirectionalAsyncChannelServer.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.test;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.util.Log;
+
+import com.android.internal.util.AsyncChannel;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Provides an interface for the server side implementation of a bidirectional channel as described
+ * in {@link com.android.internal.util.AsyncChannel}.
+ */
+public class BidirectionalAsyncChannelServer {
+
+ private static final String TAG = "BidirectionalAsyncChannelServer";
+
+ // Keeps track of incoming clients, which are identifiable by their messengers.
+ private final Map<Messenger, AsyncChannel> mClients = new HashMap<>();
+
+ private Messenger mMessenger;
+
+ public BidirectionalAsyncChannelServer(final Context context, final Looper looper,
+ final Handler messageHandler) {
+ Handler handler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncChannel channel = mClients.get(msg.replyTo);
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
+ if (channel != null) {
+ Log.d(TAG, "duplicate client connection: " + msg.sendingUid);
+ channel.replyToMessage(msg,
+ AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+ AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED);
+ } else {
+ channel = new AsyncChannel();
+ mClients.put(msg.replyTo, channel);
+ channel.connected(context, this, msg.replyTo);
+ channel.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+ AsyncChannel.STATUS_SUCCESSFUL);
+ }
+ break;
+ case AsyncChannel.CMD_CHANNEL_DISCONNECT:
+ channel.disconnect();
+ break;
+
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+ mClients.remove(msg.replyTo);
+ break;
+
+ default:
+ messageHandler.handleMessage(msg);
+ break;
+ }
+ }
+ };
+ mMessenger = new Messenger(handler);
+ }
+
+ public Messenger getMessenger() {
+ return mMessenger;
+ }
+
+}
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 9976d00..59da467 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -529,6 +529,16 @@
int openGLESVersion;
};
+static bool hasFeature(const char* name, const FeatureGroup& grp,
+ const KeyedVector<String8, ImpliedFeature>& implied) {
+ String8 name8(name);
+ ssize_t idx = grp.features.indexOfKey(name8);
+ if (idx < 0) {
+ idx = implied.indexOfKey(name8);
+ }
+ return idx >= 0;
+}
+
static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
const char* name, const char* reason, bool sdk23) {
String8 name8(name);
@@ -616,9 +626,16 @@
} else if (name == "android.hardware.location.gps" ||
name == "android.hardware.location.network") {
grp->features.add(String8("android.hardware.location"), Feature(true));
+ } else if (name == "android.hardware.faketouch.multitouch") {
+ grp->features.add(String8("android.hardware.faketouch"), Feature(true));
+ } else if (name == "android.hardware.faketouch.multitouch.distinct" ||
+ name == "android.hardware.faketouch.multitouch.jazzhands") {
+ grp->features.add(String8("android.hardware.faketouch.multitouch"), Feature(true));
+ grp->features.add(String8("android.hardware.faketouch"), Feature(true));
} else if (name == "android.hardware.touchscreen.multitouch") {
grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
- } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
+ } else if (name == "android.hardware.touchscreen.multitouch.distinct" ||
+ name == "android.hardware.touchscreen.multitouch.jazzhands") {
grp->features.add(String8("android.hardware.touchscreen.multitouch"), Feature(true));
grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
} else if (name == "android.hardware.opengles.aep") {
@@ -2005,8 +2022,12 @@
}
}
- addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
- "default feature for all apps", false);
+ // If the app hasn't declared the touchscreen as a feature requirement (either
+ // directly or implied, required or not), then the faketouch feature is implied.
+ if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) {
+ addImpliedFeature(&impliedFeatures, "android.hardware.faketouch",
+ "default feature for all apps", false);
+ }
const size_t numFeatureGroups = featureGroups.size();
if (numFeatureGroups == 0) {
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index 9939c18..aea16c7 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -808,13 +808,13 @@
assert(outPatch->paddingTop == inPatch->paddingTop);
assert(outPatch->paddingBottom == inPatch->paddingBottom);
for (int i = 0; i < outPatch->numXDivs; i++) {
- assert(outPatch->xDivs[i] == inPatch->xDivs[i]);
+ assert(outPatch->getXDivs()[i] == inPatch->getXDivs()[i]);
}
for (int i = 0; i < outPatch->numYDivs; i++) {
- assert(outPatch->yDivs[i] == inPatch->yDivs[i]);
+ assert(outPatch->getYDivs()[i] == inPatch->getYDivs()[i]);
}
for (int i = 0; i < outPatch->numColors; i++) {
- assert(outPatch->colors[i] == inPatch->colors[i]);
+ assert(outPatch->getColors()[i] == inPatch->getColors()[i]);
}
free(newData);
}
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index e640733..5f91f17 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -326,13 +326,18 @@
}
String8 resPath = it.getPath();
resPath.convertToResPath();
- table->addEntry(SourcePos(it.getPath(), 0), String16(assets->getPackage()),
+ status_t result = table->addEntry(SourcePos(it.getPath(), 0),
+ String16(assets->getPackage()),
type16,
baseName,
String16(resPath),
NULL,
&it.getParams());
- assets->addResource(it.getLeafName(), resPath, it.getFile(), type8);
+ if (result != NO_ERROR) {
+ hasErrors = true;
+ } else {
+ assets->addResource(it.getLeafName(), resPath, it.getFile(), type8);
+ }
}
return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR;
@@ -1028,7 +1033,6 @@
return NO_ERROR;
}
- ResXMLTree tree;
Asset* asset = assets.openNonAsset(cookie, "AndroidManifest.xml", Asset::ACCESS_STREAMING);
if (asset == NULL) {
fprintf(stderr, "ERROR: Platform AndroidManifest.xml not found\n");
@@ -1036,11 +1040,17 @@
}
ssize_t result = NO_ERROR;
- if (tree.setTo(asset->getBuffer(true), asset->getLength()) != NO_ERROR) {
- fprintf(stderr, "ERROR: Platform AndroidManifest.xml is corrupt\n");
- result = UNKNOWN_ERROR;
- } else {
- result = extractPlatformBuildVersion(tree, bundle);
+
+ // Create a new scope so that ResXMLTree is destroyed before we delete the memory over
+ // which it iterates (asset).
+ {
+ ResXMLTree tree;
+ if (tree.setTo(asset->getBuffer(true), asset->getLength()) != NO_ERROR) {
+ fprintf(stderr, "ERROR: Platform AndroidManifest.xml is corrupt\n");
+ result = UNKNOWN_ERROR;
+ } else {
+ result = extractPlatformBuildVersion(tree, bundle);
+ }
}
delete asset;
@@ -1370,6 +1380,10 @@
}
}
+ if (hasErrors) {
+ return UNKNOWN_ERROR;
+ }
+
// --------------------------------------------------------------------
// Assignment of resource IDs and initial generation of resource table.
// --------------------------------------------------------------------
diff --git a/tools/aapt/ResourceFilter.cpp b/tools/aapt/ResourceFilter.cpp
index 8693999..ed06f60 100644
--- a/tools/aapt/ResourceFilter.cpp
+++ b/tools/aapt/ResourceFilter.cpp
@@ -54,6 +54,46 @@
return NO_ERROR;
}
+// Returns true if the locale script of the config should be considered matching
+// the locale script of entry.
+//
+// If both the scripts are empty, the scripts are considered matching for
+// backward compatibility reasons.
+//
+// If only one script is empty, we try to compute it based on the provided
+// language and country. If we could not compute it, we assume it's either a
+// new language we don't know about, or a private use language. We return true
+// since we don't know any better and they might as well be a match.
+//
+// Finally, when we have two scripts (one of which could be computed), we return
+// true if and only if they are an exact match.
+inline bool
+scriptsMatch(const ResTable_config& config, const ResTable_config& entry) {
+ const char* configScript = config.localeScript;
+ const char* entryScript = entry.localeScript;
+ if (configScript[0] == '\0' && entryScript[0] == '\0') {
+ return true; // both scripts are empty. We match for backward compatibility reasons.
+ }
+
+ char scriptBuffer[sizeof(config.localeScript)];
+ if (configScript[0] == '\0') {
+ localeDataComputeScript(scriptBuffer, config.language, config.country);
+ if (scriptBuffer[0] == '\0') { // We can't compute the script, so we match.
+ return true;
+ }
+ configScript = scriptBuffer;
+ } else if (entryScript[0] == '\0') {
+ localeDataComputeScript(
+ scriptBuffer, entry.language, entry.country);
+ if (scriptBuffer[0] == '\0') { // We can't compute the script, so we match.
+ return true;
+ }
+ entryScript = scriptBuffer;
+ }
+ return (memcmp(configScript, entryScript, sizeof(config.localeScript)) == 0);
+}
+
+
bool
WeakResourceFilter::match(const ResTable_config& config) const
{
@@ -75,11 +115,19 @@
// If the locales differ, but the languages are the same and
// the locale we are matching only has a language specified,
// we match.
- if (config.language[0] &&
- memcmp(config.language, entry.first.language, sizeof(config.language)) == 0) {
- if (config.country[0] == 0) {
- matchedAxis |= ResTable_config::CONFIG_LOCALE;
- }
+ //
+ // Exception: we won't match if a script is specified for at least
+ // one of the locales and it's different from the other locale's
+ // script. (We will compute the other script if at least one of the
+ // scripts were explicitly set. In cases we can't compute an script,
+ // we match.)
+ if (config.language[0] != '\0' &&
+ config.country[0] == '\0' &&
+ config.localeVariant[0] == '\0' &&
+ config.language[0] == entry.first.language[0] &&
+ config.language[1] == entry.first.language[1] &&
+ scriptsMatch(config, entry.first)) {
+ matchedAxis |= ResTable_config::CONFIG_LOCALE;
}
} else if ((diff & entry.second) == ResTable_config::CONFIG_SMALLEST_SCREEN_SIZE) {
// Special case if the smallest screen width doesn't match. We check that the
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 6a4b637..9cae00f 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2623,6 +2623,14 @@
const SourcePos unknown(String8("????"), 0);
sp<Type> attr = p->getType(String16("attr"), unknown);
+ // Force creation of ID if we are building feature splits.
+ // Auto-generated ID resources won't apply the type ID offset correctly unless
+ // the offset is applied here first.
+ // b/30607637
+ if (mPackageType == AppFeature && p->getName() == mAssetsPackage) {
+ sp<Type> id = p->getType(String16("id"), unknown);
+ }
+
// Assign indices...
const size_t typeCount = p->getOrderedTypes().size();
for (size_t ti = 0; ti < typeCount; ti++) {
@@ -4521,6 +4529,7 @@
const ConfigDescription& sourceConfig,
const int sdkVersionToGenerate) {
assert(sdkVersionToGenerate > sourceConfig.sdkVersion);
+ assert(configList != NULL);
const DefaultKeyedVector<ConfigDescription, sp<ResourceTable::Entry>>& entries
= configList->getEntries();
ssize_t idx = entries.indexOfKey(sourceConfig);
@@ -4851,24 +4860,39 @@
const Vector<sp<Type> >& types = mOrderedPackages[p]->getOrderedTypes();
const size_t typeCount = types.size();
for (size_t t = 0; t < typeCount; t++) {
- const Vector<sp<ConfigList> >& configs = types[t]->getOrderedConfigs();
+ const sp<Type>& type = types[t];
+ if (type == NULL) {
+ continue;
+ }
+
+ const Vector<sp<ConfigList> >& configs = type->getOrderedConfigs();
const size_t configCount = configs.size();
for (size_t c = 0; c < configCount; c++) {
+ const sp<ConfigList>& configList = configs[c];
+ if (configList == NULL) {
+ continue;
+ }
+
const DefaultKeyedVector<ConfigDescription, sp<Entry> >& configEntries
- = configs[c]->getEntries();
+ = configList->getEntries();
const size_t configEntryCount = configEntries.size();
for (size_t ce = 0; ce < configEntryCount; ce++) {
+ const sp<Entry>& entry = configEntries.valueAt(ce);
+ if (entry == NULL) {
+ continue;
+ }
+
const ConfigDescription& config = configEntries.keyAt(ce);
if (AaptConfig::isDensityOnly(config)) {
// This configuration only varies with regards to density.
const Symbol symbol(
mOrderedPackages[p]->getName(),
- types[t]->getName(),
- configs[c]->getName(),
+ type->getName(),
+ configList->getName(),
getResId(mOrderedPackages[p], types[t],
- configs[c]->getEntryIndex()));
+ configList->getEntryIndex()));
- const sp<Entry>& entry = configEntries.valueAt(ce);
+
AaptUtil::appendValue(resources, symbol,
SymbolDefinition(symbol, config, entry->getPos()));
}
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 3a1e2bb..4b5ea65 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -115,6 +115,7 @@
toolSources := \
compile/Compile.cpp \
+ diff/Diff.cpp \
dump/Dump.cpp \
link/Link.cpp
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index a2fadd9..00d8aae 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -24,6 +24,7 @@
extern int compile(const std::vector<StringPiece>& args);
extern int link(const std::vector<StringPiece>& args);
extern int dump(const std::vector<StringPiece>& args);
+extern int diff(const std::vector<StringPiece>& args);
} // namespace aapt
@@ -44,12 +45,14 @@
return aapt::link(args);
} else if (command == "dump" || command == "d") {
return aapt::dump(args);
+ } else if (command == "diff") {
+ return aapt::diff(args);
}
std::cerr << "unknown command '" << command << "'\n";
} else {
std::cerr << "no command specified\n";
}
- std::cerr << "\nusage: aapt2 [compile|link|dump] ..." << std::endl;
+ std::cerr << "\nusage: aapt2 [compile|link|dump|diff] ..." << std::endl;
return 1;
}
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 9704d970..a84c306 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -152,7 +152,7 @@
break;
}
- spanStack.back().lastChar = builder.str().size();
+ spanStack.back().lastChar = builder.str().size() - 1;
outStyleString->spans.push_back(spanStack.back());
spanStack.pop_back();
@@ -1058,6 +1058,16 @@
std::unique_ptr<Array> array = util::make_unique<Array>();
+ bool translateable = mOptions.translatable;
+ if (Maybe<StringPiece16> translateableAttr = xml::findAttribute(parser, u"translatable")) {
+ if (!ResourceUtils::tryParseBool(translateableAttr.value(), &translateable)) {
+ mDiag->error(DiagMessage(outResource->source)
+ << "invalid value for 'translatable'. Must be a boolean");
+ return false;
+ }
+ }
+ array->setTranslateable(translateable);
+
bool error = false;
const size_t depth = parser->getDepth();
while (xml::XmlPullParser::nextChildNode(parser, depth)) {
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 8d734f3..e700ed9 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -189,6 +189,17 @@
return results;
}
+std::vector<ResourceConfigValue*> ResourceEntry::findValuesIf(
+ const std::function<bool(ResourceConfigValue*)>& f) {
+ std::vector<ResourceConfigValue*> results;
+ for (auto& configValue : values) {
+ if (f(configValue.get())) {
+ results.push_back(configValue.get());
+ }
+ }
+ return results;
+}
+
/**
* The default handler for collisions. A return value of -1 means keep the
* existing value, 0 means fail, and +1 means take the incoming value.
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 7f5c2b8..5690ea6 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -26,6 +26,7 @@
#include "io/File.h"
#include <android-base/macros.h>
+#include <functional>
#include <map>
#include <memory>
#include <string>
@@ -109,6 +110,9 @@
ResourceConfigValue* findOrCreateValue(const ConfigDescription& config,
const StringPiece& product);
std::vector<ResourceConfigValue*> findAllValues(const ConfigDescription& config);
+ std::vector<ResourceConfigValue*> findValuesIf(
+ const std::function<bool(ResourceConfigValue*)>& f);
+
private:
DISALLOW_COPY_AND_ASSIGN(ResourceEntry);
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 74c48b0..a0a7efc 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -289,7 +289,7 @@
std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* flagAttr,
const StringPiece16& str) {
android::Res_value flags = { };
- flags.dataType = android::Res_value::TYPE_INT_DEC;
+ flags.dataType = android::Res_value::TYPE_INT_HEX;
flags.data = 0u;
if (util::trimWhitespace(str).empty()) {
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index dd7ff01..c10b134 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -39,6 +39,14 @@
RawString::RawString(const StringPool::Ref& ref) : value(ref) {
}
+bool RawString::equals(const Value* value) const {
+ const RawString* other = valueCast<RawString>(value);
+ if (!other) {
+ return false;
+ }
+ return *this->value == *other->value;
+}
+
RawString* RawString::clone(StringPool* newPool) const {
RawString* rs = new RawString(newPool->makeRef(*value));
rs->mComment = mComment;
@@ -66,6 +74,15 @@
Reference::Reference(const ResourceId& i, Type type) : id(i), referenceType(type) {
}
+bool Reference::equals(const Value* value) const {
+ const Reference* other = valueCast<Reference>(value);
+ if (!other) {
+ return false;
+ }
+ return referenceType == other->referenceType && privateReference == other->privateReference &&
+ id == other->id && name == other->name;
+}
+
bool Reference::flatten(android::Res_value* outValue) const {
outValue->dataType = (referenceType == Reference::Type::kResource) ?
android::Res_value::TYPE_REFERENCE : android::Res_value::TYPE_ATTRIBUTE;
@@ -97,6 +114,10 @@
}
}
+bool Id::equals(const Value* value) const {
+ return valueCast<Id>(value) != nullptr;
+}
+
bool Id::flatten(android::Res_value* out) const {
out->dataType = android::Res_value::TYPE_INT_BOOLEAN;
out->data = util::hostToDevice32(0);
@@ -111,15 +132,15 @@
*out << "(id)";
}
-String::String(const StringPool::Ref& ref) : value(ref), mTranslateable(true) {
+String::String(const StringPool::Ref& ref) : value(ref) {
}
-void String::setTranslateable(bool val) {
- mTranslateable = val;
-}
-
-bool String::isTranslateable() const {
- return mTranslateable;
+bool String::equals(const Value* value) const {
+ const String* other = valueCast<String>(value);
+ if (!other) {
+ return false;
+ }
+ return *this->value == *other->value;
}
bool String::flatten(android::Res_value* outValue) const {
@@ -144,15 +165,24 @@
*out << "(string) \"" << *value << "\"";
}
-StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref), mTranslateable(true) {
+StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref) {
}
-void StyledString::setTranslateable(bool val) {
- mTranslateable = val;
-}
+bool StyledString::equals(const Value* value) const {
+ const StyledString* other = valueCast<StyledString>(value);
+ if (!other) {
+ return false;
+ }
-bool StyledString::isTranslateable() const {
- return mTranslateable;
+ if (*this->value->str == *other->value->str) {
+ const std::vector<StringPool::Span>& spansA = this->value->spans;
+ const std::vector<StringPool::Span>& spansB = other->value->spans;
+ return std::equal(spansA.begin(), spansA.end(), spansB.begin(),
+ [](const StringPool::Span& a, const StringPool::Span& b) -> bool {
+ return *a.name == *b.name && a.firstChar == b.firstChar && a.lastChar == b.lastChar;
+ });
+ }
+ return false;
}
bool StyledString::flatten(android::Res_value* outValue) const {
@@ -174,11 +204,22 @@
void StyledString::print(std::ostream* out) const {
*out << "(styled string) \"" << *value->str << "\"";
+ for (const StringPool::Span& span : value->spans) {
+ *out << " "<< *span.name << ":" << span.firstChar << "," << span.lastChar;
+ }
}
FileReference::FileReference(const StringPool::Ref& _path) : path(_path) {
}
+bool FileReference::equals(const Value* value) const {
+ const FileReference* other = valueCast<FileReference>(value);
+ if (!other) {
+ return false;
+ }
+ return *path == *other->path;
+}
+
bool FileReference::flatten(android::Res_value* outValue) const {
if (path.getIndex() > std::numeric_limits<uint32_t>::max()) {
return false;
@@ -209,6 +250,14 @@
value.data = data;
}
+bool BinaryPrimitive::equals(const Value* value) const {
+ const BinaryPrimitive* other = valueCast<BinaryPrimitive>(value);
+ if (!other) {
+ return false;
+ }
+ return this->value.dataType == other->value.dataType && this->value.data == other->value.data;
+}
+
bool BinaryPrimitive::flatten(android::Res_value* outValue) const {
outValue->dataType = value.dataType;
outValue->data = util::hostToDevice32(value.data);
@@ -228,7 +277,7 @@
*out << "(integer) " << static_cast<int32_t>(value.data);
break;
case android::Res_value::TYPE_INT_HEX:
- *out << "(integer) " << std::hex << value.data << std::dec;
+ *out << "(integer) 0x" << std::hex << value.data << std::dec;
break;
case android::Res_value::TYPE_INT_BOOLEAN:
*out << "(boolean) " << (value.data != 0 ? "true" : "false");
@@ -253,6 +302,21 @@
mWeak = w;
}
+bool Attribute::equals(const Value* value) const {
+ const Attribute* other = valueCast<Attribute>(value);
+ if (!other) {
+ return false;
+ }
+
+ return this->typeMask == other->typeMask && this->minInt == other->minInt &&
+ this->maxInt == other->maxInt &&
+ std::equal(this->symbols.begin(), this->symbols.end(),
+ other->symbols.begin(),
+ [](const Symbol& a, const Symbol& b) -> bool {
+ return a.symbol.equals(&b.symbol) && a.value == b.value;
+ });
+}
+
Attribute* Attribute::clone(StringPool* /*newPool*/) const {
return new Attribute(*this);
}
@@ -365,6 +429,14 @@
<< "]";
}
+ if (minInt != std::numeric_limits<int32_t>::min()) {
+ *out << " min=" << minInt;
+ }
+
+ if (maxInt != std::numeric_limits<int32_t>::max()) {
+ *out << " max=" << maxInt;
+ }
+
if (isWeak()) {
*out << " [weak]";
}
@@ -445,6 +517,21 @@
return true;
}
+bool Style::equals(const Value* value) const {
+ const Style* other = valueCast<Style>(value);
+ if (!other) {
+ return false;
+ }
+ if (bool(parent) != bool(other->parent) ||
+ (parent && other->parent && !parent.value().equals(&other->parent.value()))) {
+ return false;
+ }
+ return std::equal(entries.begin(), entries.end(), other->entries.begin(),
+ [](const Entry& a, const Entry& b) -> bool {
+ return a.key.equals(&b.key) && a.value->equals(b.value.get());
+ });
+}
+
Style* Style::clone(StringPool* newPool) const {
Style* style = new Style();
style->parent = parent;
@@ -484,6 +571,18 @@
return out;
}
+bool Array::equals(const Value* value) const {
+ const Array* other = valueCast<Array>(value);
+ if (!other) {
+ return false;
+ }
+
+ return std::equal(items.begin(), items.end(), other->items.begin(),
+ [](const std::unique_ptr<Item>& a, const std::unique_ptr<Item>& b) -> bool {
+ return a->equals(b.get());
+ });
+}
+
Array* Array::clone(StringPool* newPool) const {
Array* array = new Array();
array->mComment = mComment;
@@ -500,6 +599,21 @@
<< "]";
}
+bool Plural::equals(const Value* value) const {
+ const Plural* other = valueCast<Plural>(value);
+ if (!other) {
+ return false;
+ }
+
+ return std::equal(values.begin(), values.end(), other->values.begin(),
+ [](const std::unique_ptr<Item>& a, const std::unique_ptr<Item>& b) -> bool {
+ if (bool(a) != bool(b)) {
+ return false;
+ }
+ return bool(a) == bool(b) || a->equals(b.get());
+ });
+}
+
Plural* Plural::clone(StringPool* newPool) const {
Plural* p = new Plural();
p->mComment = mComment;
@@ -515,12 +629,42 @@
void Plural::print(std::ostream* out) const {
*out << "(plural)";
+ if (values[Zero]) {
+ *out << " zero=" << *values[Zero];
+ }
+
+ if (values[One]) {
+ *out << " one=" << *values[One];
+ }
+
+ if (values[Two]) {
+ *out << " two=" << *values[Two];
+ }
+
+ if (values[Few]) {
+ *out << " few=" << *values[Few];
+ }
+
+ if (values[Many]) {
+ *out << " many=" << *values[Many];
+ }
}
static ::std::ostream& operator<<(::std::ostream& out, const std::unique_ptr<Item>& item) {
return out << *item;
}
+bool Styleable::equals(const Value* value) const {
+ const Styleable* other = valueCast<Styleable>(value);
+ if (!other) {
+ return false;
+ }
+ return std::equal(entries.begin(), entries.end(), other->entries.begin(),
+ [](const Reference& a, const Reference& b) -> bool {
+ return a.equals(&b);
+ });
+}
+
Styleable* Styleable::clone(StringPool* /*newPool*/) const {
return new Styleable(*this);
}
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 43354ac..aa1b550 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -54,6 +54,18 @@
mWeak = val;
}
+ // Whether the value is marked as translateable.
+ // This does not persist when flattened.
+ // It is only used during compilation phase.
+ void setTranslateable(bool val) {
+ mTranslateable = val;
+ }
+
+ // Default true.
+ bool isTranslateable() const {
+ return mTranslateable;
+ }
+
/**
* Returns the source where this value was defined.
*/
@@ -84,6 +96,8 @@
mComment = std::move(str);
}
+ virtual bool equals(const Value* value) const = 0;
+
/**
* Calls the appropriate overload of ValueVisitor.
*/
@@ -103,6 +117,7 @@
Source mSource;
std::u16string mComment;
bool mWeak = false;
+ bool mTranslateable = true;
};
/**
@@ -158,6 +173,7 @@
explicit Reference(const ResourceNameRef& n, Type type = Type::kResource);
explicit Reference(const ResourceId& i, Type type = Type::kResource);
+ bool equals(const Value* value) const override;
bool flatten(android::Res_value* outValue) const override;
Reference* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
@@ -168,6 +184,7 @@
*/
struct Id : public BaseItem<Id> {
Id() { mWeak = true; }
+ bool equals(const Value* value) const override;
bool flatten(android::Res_value* out) const override;
Id* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
@@ -183,6 +200,7 @@
RawString(const StringPool::Ref& ref);
+ bool equals(const Value* value) const override;
bool flatten(android::Res_value* outValue) const override;
RawString* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
@@ -193,17 +211,10 @@
String(const StringPool::Ref& ref);
- // Whether the string is marked as translateable. This does not persist when flattened.
- // It is only used during compilation phase.
- void setTranslateable(bool val);
- bool isTranslateable() const;
-
+ bool equals(const Value* value) const override;
bool flatten(android::Res_value* outValue) const override;
String* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
-
-private:
- bool mTranslateable;
};
struct StyledString : public BaseItem<StyledString> {
@@ -211,17 +222,10 @@
StyledString(const StringPool::StyleRef& ref);
- // Whether the string is marked as translateable. This does not persist when flattened.
- // It is only used during compilation phase.
- void setTranslateable(bool val);
- bool isTranslateable() const;
-
+ bool equals(const Value* value) const override;
bool flatten(android::Res_value* outValue) const override;
StyledString* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
-
-private:
- bool mTranslateable;
};
struct FileReference : public BaseItem<FileReference> {
@@ -235,6 +239,7 @@
FileReference() = default;
FileReference(const StringPool::Ref& path);
+ bool equals(const Value* value) const override;
bool flatten(android::Res_value* outValue) const override;
FileReference* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
@@ -250,6 +255,7 @@
BinaryPrimitive(const android::Res_value& val);
BinaryPrimitive(uint8_t dataType, uint32_t data);
+ bool equals(const Value* value) const override;
bool flatten(android::Res_value* outValue) const override;
BinaryPrimitive* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
@@ -268,6 +274,7 @@
Attribute(bool w, uint32_t t = 0u);
+ bool equals(const Value* value) const override;
Attribute* clone(StringPool* newPool) const override;
void printMask(std::ostream* out) const;
void print(std::ostream* out) const override;
@@ -290,6 +297,7 @@
std::vector<Entry> entries;
+ bool equals(const Value* value) const override;
Style* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
};
@@ -297,6 +305,7 @@
struct Array : public BaseValue<Array> {
std::vector<std::unique_ptr<Item>> items;
+ bool equals(const Value* value) const override;
Array* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
};
@@ -314,6 +323,7 @@
std::array<std::unique_ptr<Item>, Count> values;
+ bool equals(const Value* value) const override;
Plural* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
};
@@ -321,6 +331,7 @@
struct Styleable : public BaseValue<Styleable> {
std::vector<Reference> entries;
+ bool equals(const Value* value) const override;
Styleable* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
};
diff --git a/tools/aapt2/ValueVisitor.h b/tools/aapt2/ValueVisitor.h
index ea2aa55..b8bc5db 100644
--- a/tools/aapt2/ValueVisitor.h
+++ b/tools/aapt2/ValueVisitor.h
@@ -127,6 +127,11 @@
}
};
+template <typename T>
+const T* valueCast(const Value* value) {
+ return valueCast<T>(const_cast<Value*>(value));
+}
+
/**
* Returns a valid pointer to T if the Value is of subtype T.
* Otherwise, returns nullptr.
@@ -141,7 +146,6 @@
return visitor.value;
}
-
inline void visitAllValuesInPackage(ResourceTablePackage* pkg, RawValueVisitor* visitor) {
for (auto& type : pkg->types) {
for (auto& entry : type->entries) {
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp
index 99c2077..d080e16 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp
@@ -128,23 +128,6 @@
mPool(pool), mMethod(method), mLocalizer(method) {
}
- void visit(Array* array) override {
- std::unique_ptr<Array> localized = util::make_unique<Array>();
- localized->items.resize(array->items.size());
- for (size_t i = 0; i < array->items.size(); i++) {
- Visitor subVisitor(mPool, mMethod);
- array->items[i]->accept(&subVisitor);
- if (subVisitor.mItem) {
- localized->items[i] = std::move(subVisitor.mItem);
- } else {
- localized->items[i] = std::unique_ptr<Item>(array->items[i]->clone(mPool));
- }
- }
- localized->setSource(array->getSource());
- localized->setWeak(true);
- mValue = std::move(localized);
- }
-
void visit(Plural* plural) override {
std::unique_ptr<Plural> localized = util::make_unique<Plural>();
for (size_t i = 0; i < plural->values.size(); i++) {
@@ -164,10 +147,6 @@
}
void visit(String* string) override {
- if (!string->isTranslateable()) {
- return;
- }
-
std::u16string result = mLocalizer.start() + mLocalizer.text(*string->value) +
mLocalizer.end();
std::unique_ptr<String> localized = util::make_unique<String>(mPool->makeRef(result));
@@ -177,10 +156,6 @@
}
void visit(StyledString* string) override {
- if (!string->isTranslateable()) {
- return;
- }
-
mItem = pseudolocalizeStyledString(string, mMethod, mPool);
mItem->setWeak(true);
}
@@ -238,14 +213,26 @@
}
}
+/**
+ * A value is pseudolocalizable if it does not define a locale (or is the default locale)
+ * and is translateable.
+ */
+static bool isPseudolocalizable(ResourceConfigValue* configValue) {
+ const int diff = configValue->config.diff(ConfigDescription::defaultConfig());
+ if (diff & ConfigDescription::CONFIG_LOCALE) {
+ return false;
+ }
+ return configValue->value->isTranslateable();
+}
+
} // namespace
bool PseudolocaleGenerator::consume(IAaptContext* context, ResourceTable* table) {
for (auto& package : table->packages) {
for (auto& type : package->types) {
for (auto& entry : type->entries) {
- std::vector<ResourceConfigValue*> values = entry->findAllValues(
- ConfigDescription::defaultConfig());
+ std::vector<ResourceConfigValue*> values = entry->findValuesIf(isPseudolocalizable);
+
for (ResourceConfigValue* value : values) {
pseudolocalizeIfNeeded(Pseudolocalizer::Method::kAccent, value,
&table->stringPool, entry.get());
diff --git a/tools/aapt2/diff/Diff.cpp b/tools/aapt2/diff/Diff.cpp
new file mode 100644
index 0000000..20b7b59
--- /dev/null
+++ b/tools/aapt2/diff/Diff.cpp
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Flags.h"
+#include "ResourceTable.h"
+#include "io/ZipArchive.h"
+#include "process/IResourceTableConsumer.h"
+#include "process/SymbolTable.h"
+#include "unflatten/BinaryResourceParser.h"
+
+#include <android-base/macros.h>
+
+namespace aapt {
+
+class DiffContext : public IAaptContext {
+public:
+ const std::u16string& getCompilationPackage() override {
+ return mEmpty;
+ }
+
+ uint8_t getPackageId() override {
+ return 0x0;
+ }
+
+ IDiagnostics* getDiagnostics() override {
+ return &mDiagnostics;
+ }
+
+ NameMangler* getNameMangler() override {
+ return &mNameMangler;
+ }
+
+ SymbolTable* getExternalSymbols() override {
+ return &mSymbolTable;
+ }
+
+ bool verbose() override {
+ return false;
+ }
+
+private:
+ std::u16string mEmpty;
+ StdErrDiagnostics mDiagnostics;
+ NameMangler mNameMangler = NameMangler(NameManglerPolicy{});
+ SymbolTable mSymbolTable;
+};
+
+class LoadedApk {
+public:
+ LoadedApk(const Source& source, std::unique_ptr<io::IFileCollection> apk,
+ std::unique_ptr<ResourceTable> table) :
+ mSource(source), mApk(std::move(apk)), mTable(std::move(table)) {
+ }
+
+ io::IFileCollection* getFileCollection() {
+ return mApk.get();
+ }
+
+ ResourceTable* getResourceTable() {
+ return mTable.get();
+ }
+
+ const Source& getSource() {
+ return mSource;
+ }
+
+private:
+ Source mSource;
+ std::unique_ptr<io::IFileCollection> mApk;
+ std::unique_ptr<ResourceTable> mTable;
+
+ DISALLOW_COPY_AND_ASSIGN(LoadedApk);
+};
+
+static std::unique_ptr<LoadedApk> loadApkFromPath(IAaptContext* context, const StringPiece& path) {
+ Source source(path);
+ std::string error;
+ std::unique_ptr<io::ZipFileCollection> apk = io::ZipFileCollection::create(path, &error);
+ if (!apk) {
+ context->getDiagnostics()->error(DiagMessage(source) << error);
+ return {};
+ }
+
+ io::IFile* file = apk->findFile("resources.arsc");
+ if (!file) {
+ context->getDiagnostics()->error(DiagMessage(source) << "no resources.arsc found");
+ return {};
+ }
+
+ std::unique_ptr<io::IData> data = file->openAsData();
+ if (!data) {
+ context->getDiagnostics()->error(DiagMessage(source) << "could not open resources.arsc");
+ return {};
+ }
+
+ std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
+ BinaryResourceParser parser(context, table.get(), source, data->data(), data->size());
+ if (!parser.parse()) {
+ return {};
+ }
+
+ return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table));
+}
+
+static void emitDiffLine(const Source& source, const StringPiece& message) {
+ std::cerr << source << ": " << message << "\n";
+}
+
+static bool isSymbolVisibilityDifferent(const Symbol& symbolA, const Symbol& symbolB) {
+ return symbolA.state != symbolB.state;
+}
+
+template <typename Id>
+static bool isIdDiff(const Symbol& symbolA, const Maybe<Id>& idA,
+ const Symbol& symbolB, const Maybe<Id>& idB) {
+ if (symbolA.state == SymbolState::kPublic || symbolB.state == SymbolState::kPublic) {
+ return idA != idB;
+ }
+ return false;
+}
+
+static bool emitResourceConfigValueDiff(IAaptContext* context,
+ LoadedApk* apkA,
+ ResourceTablePackage* pkgA,
+ ResourceTableType* typeA,
+ ResourceEntry* entryA,
+ ResourceConfigValue* configValueA,
+ LoadedApk* apkB,
+ ResourceTablePackage* pkgB,
+ ResourceTableType* typeB,
+ ResourceEntry* entryB,
+ ResourceConfigValue* configValueB) {
+ Value* valueA = configValueA->value.get();
+ Value* valueB = configValueB->value.get();
+ if (!valueA->equals(valueB)) {
+ std::stringstream strStream;
+ strStream << "value " << pkgA->name << ":" << typeA->type << "/" << entryA->name
+ << " config=" << configValueA->config << " does not match:\n";
+ valueA->print(&strStream);
+ strStream << "\n vs \n";
+ valueB->print(&strStream);
+ emitDiffLine(apkB->getSource(), strStream.str());
+ return true;
+ }
+ return false;
+}
+
+static bool emitResourceEntryDiff(IAaptContext* context,
+ LoadedApk* apkA,
+ ResourceTablePackage* pkgA,
+ ResourceTableType* typeA,
+ ResourceEntry* entryA,
+ LoadedApk* apkB,
+ ResourceTablePackage* pkgB,
+ ResourceTableType* typeB,
+ ResourceEntry* entryB) {
+ bool diff = false;
+ for (std::unique_ptr<ResourceConfigValue>& configValueA : entryA->values) {
+ ResourceConfigValue* configValueB = entryB->findValue(configValueA->config);
+ if (!configValueB) {
+ std::stringstream strStream;
+ strStream << "missing " << pkgA->name << ":" << typeA->type << "/" << entryA->name
+ << " config=" << configValueA->config;
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
+ } else {
+ diff |= emitResourceConfigValueDiff(context, apkA, pkgA, typeA, entryA,
+ configValueA.get(), apkB, pkgB, typeB, entryB,
+ configValueB);
+ }
+ }
+
+ // Check for any newly added config values.
+ for (std::unique_ptr<ResourceConfigValue>& configValueB : entryB->values) {
+ ResourceConfigValue* configValueA = entryA->findValue(configValueB->config);
+ if (!configValueA) {
+ std::stringstream strStream;
+ strStream << "new config " << pkgB->name << ":" << typeB->type << "/" << entryB->name
+ << " config=" << configValueB->config;
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
+ }
+ }
+ return false;
+}
+
+static bool emitResourceTypeDiff(IAaptContext* context,
+ LoadedApk* apkA,
+ ResourceTablePackage* pkgA,
+ ResourceTableType* typeA,
+ LoadedApk* apkB,
+ ResourceTablePackage* pkgB,
+ ResourceTableType* typeB) {
+ bool diff = false;
+ for (std::unique_ptr<ResourceEntry>& entryA : typeA->entries) {
+ ResourceEntry* entryB = typeB->findEntry(entryA->name);
+ if (!entryB) {
+ std::stringstream strStream;
+ strStream << "missing " << pkgA->name << ":" << typeA->type << "/" << entryA->name;
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
+ } else {
+ if (isSymbolVisibilityDifferent(entryA->symbolStatus, entryB->symbolStatus)) {
+ std::stringstream strStream;
+ strStream << pkgA->name << ":" << typeA->type << "/" << entryA->name
+ << " has different visibility (";
+ if (entryB->symbolStatus.state == SymbolState::kPublic) {
+ strStream << "PUBLIC";
+ } else {
+ strStream << "PRIVATE";
+ }
+ strStream << " vs ";
+ if (entryA->symbolStatus.state == SymbolState::kPublic) {
+ strStream << "PUBLIC";
+ } else {
+ strStream << "PRIVATE";
+ }
+ strStream << ")";
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
+ } else if (isIdDiff(entryA->symbolStatus, entryA->id,
+ entryB->symbolStatus, entryB->id)) {
+ std::stringstream strStream;
+ strStream << pkgA->name << ":" << typeA->type << "/" << entryA->name
+ << " has different public ID (";
+ if (entryB->id) {
+ strStream << "0x" << std::hex << entryB->id.value();
+ } else {
+ strStream << "none";
+ }
+ strStream << " vs ";
+ if (entryA->id) {
+ strStream << "0x " << std::hex << entryA->id.value();
+ } else {
+ strStream << "none";
+ }
+ strStream << ")";
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
+ }
+ diff |= emitResourceEntryDiff(context, apkA, pkgA, typeA, entryA.get(),
+ apkB, pkgB, typeB, entryB);
+ }
+ }
+
+ // Check for any newly added entries.
+ for (std::unique_ptr<ResourceEntry>& entryB : typeB->entries) {
+ ResourceEntry* entryA = typeA->findEntry(entryB->name);
+ if (!entryA) {
+ std::stringstream strStream;
+ strStream << "new entry " << pkgB->name << ":" << typeB->type << "/" << entryB->name;
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
+ }
+ }
+ return diff;
+}
+
+static bool emitResourcePackageDiff(IAaptContext* context, LoadedApk* apkA,
+ ResourceTablePackage* pkgA,
+ LoadedApk* apkB, ResourceTablePackage* pkgB) {
+ bool diff = false;
+ for (std::unique_ptr<ResourceTableType>& typeA : pkgA->types) {
+ ResourceTableType* typeB = pkgB->findType(typeA->type);
+ if (!typeB) {
+ std::stringstream strStream;
+ strStream << "missing " << pkgA->name << ":" << typeA->type;
+ emitDiffLine(apkA->getSource(), strStream.str());
+ diff = true;
+ } else {
+ if (isSymbolVisibilityDifferent(typeA->symbolStatus, typeB->symbolStatus)) {
+ std::stringstream strStream;
+ strStream << pkgA->name << ":" << typeA->type << " has different visibility (";
+ if (typeB->symbolStatus.state == SymbolState::kPublic) {
+ strStream << "PUBLIC";
+ } else {
+ strStream << "PRIVATE";
+ }
+ strStream << " vs ";
+ if (typeA->symbolStatus.state == SymbolState::kPublic) {
+ strStream << "PUBLIC";
+ } else {
+ strStream << "PRIVATE";
+ }
+ strStream << ")";
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
+ } else if (isIdDiff(typeA->symbolStatus, typeA->id, typeB->symbolStatus, typeB->id)) {
+ std::stringstream strStream;
+ strStream << pkgA->name << ":" << typeA->type << " has different public ID (";
+ if (typeB->id) {
+ strStream << "0x" << std::hex << typeB->id.value();
+ } else {
+ strStream << "none";
+ }
+ strStream << " vs ";
+ if (typeA->id) {
+ strStream << "0x " << std::hex << typeA->id.value();
+ } else {
+ strStream << "none";
+ }
+ strStream << ")";
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
+ }
+ diff |= emitResourceTypeDiff(context, apkA, pkgA, typeA.get(), apkB, pkgB, typeB);
+ }
+ }
+
+ // Check for any newly added types.
+ for (std::unique_ptr<ResourceTableType>& typeB : pkgB->types) {
+ ResourceTableType* typeA = pkgA->findType(typeB->type);
+ if (!typeA) {
+ std::stringstream strStream;
+ strStream << "new type " << pkgB->name << ":" << typeB->type;
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
+ }
+ }
+ return diff;
+}
+
+static bool emitResourceTableDiff(IAaptContext* context, LoadedApk* apkA, LoadedApk* apkB) {
+ ResourceTable* tableA = apkA->getResourceTable();
+ ResourceTable* tableB = apkB->getResourceTable();
+
+ bool diff = false;
+ for (std::unique_ptr<ResourceTablePackage>& pkgA : tableA->packages) {
+ ResourceTablePackage* pkgB = tableB->findPackage(pkgA->name);
+ if (!pkgB) {
+ std::stringstream strStream;
+ strStream << "missing package " << pkgA->name;
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
+ } else {
+ if (pkgA->id != pkgB->id) {
+ std::stringstream strStream;
+ strStream << "package '" << pkgA->name << "' has different id (";
+ if (pkgB->id) {
+ strStream << "0x" << std::hex << pkgB->id.value();
+ } else {
+ strStream << "none";
+ }
+ strStream << " vs ";
+ if (pkgA->id) {
+ strStream << "0x" << std::hex << pkgA->id.value();
+ } else {
+ strStream << "none";
+ }
+ strStream << ")";
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
+ }
+ diff |= emitResourcePackageDiff(context, apkA, pkgA.get(), apkB, pkgB);
+ }
+ }
+
+ // Check for any newly added packages.
+ for (std::unique_ptr<ResourceTablePackage>& pkgB : tableB->packages) {
+ ResourceTablePackage* pkgA = tableA->findPackage(pkgB->name);
+ if (!pkgA) {
+ std::stringstream strStream;
+ strStream << "new package " << pkgB->name;
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
+ }
+ }
+ return diff;
+}
+
+int diff(const std::vector<StringPiece>& args) {
+ DiffContext context;
+
+ Flags flags;
+ if (!flags.parse("aapt2 diff", args, &std::cerr)) {
+ return 1;
+ }
+
+ if (flags.getArgs().size() != 2u) {
+ std::cerr << "must have two apks as arguments.\n\n";
+ flags.usage("aapt2 diff", &std::cerr);
+ return 1;
+ }
+
+ std::unique_ptr<LoadedApk> apkA = loadApkFromPath(&context, flags.getArgs()[0]);
+ std::unique_ptr<LoadedApk> apkB = loadApkFromPath(&context, flags.getArgs()[1]);
+ if (!apkA || !apkB) {
+ return 1;
+ }
+
+ if (emitResourceTableDiff(&context, apkA.get(), apkB.get())) {
+ // We emitted a diff, so return 1 (failure).
+ return 1;
+ }
+ return 0;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 953e87e..77a949f 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -180,7 +180,6 @@
manifestAction[u"uses-configuration"];
manifestAction[u"uses-feature"];
- manifestAction[u"uses-library"];
manifestAction[u"supports-screens"];
manifestAction[u"compatible-screens"];
manifestAction[u"supports-gl-texture"];
@@ -189,6 +188,9 @@
xml::XmlNodeAction& applicationAction = (*executor)[u"manifest"][u"application"];
applicationAction.action(optionalNameIsJavaClassName);
+ // Uses library actions.
+ applicationAction[u"uses-library"];
+
// Activity actions.
applicationAction[u"activity"].action(requiredNameIsJavaClassName);
applicationAction[u"activity"][u"intent-filter"] = intentFilterAction;
diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp
index 82e4fb0..1ec48f0 100644
--- a/tools/aapt2/proto/TableProtoDeserializer.cpp
+++ b/tools/aapt2/proto/TableProtoDeserializer.cpp
@@ -234,6 +234,8 @@
const pb::Attribute& pbAttr = pbCompoundValue.attr();
std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);
attr->typeMask = pbAttr.format_flags();
+ attr->minInt = pbAttr.min_int();
+ attr->maxInt = pbAttr.max_int();
for (const pb::Attribute_Symbol& pbSymbol : pbAttr.symbols()) {
Attribute::Symbol symbol;
deserializeItemCommon(pbSymbol, &symbol.symbol);
diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py
index ea36e2c..2956d87 100755
--- a/tools/fonts/fontchain_lint.py
+++ b/tools/fonts/fontchain_lint.py
@@ -256,23 +256,33 @@
def check_emoji_coverage(all_emoji, equivalent_emoji):
+ emoji_font = get_emoji_font()
+ check_emoji_font_coverage(emoji_font, all_emoji, equivalent_emoji)
+
+
+def get_emoji_font():
emoji_fonts = [
record.font for record in _fallback_chain
if 'Zsye' in record.scripts]
assert len(emoji_fonts) == 1, 'There are %d emoji fonts.' % len(emoji_fonts)
- emoji_font = emoji_fonts[0]
- coverage = get_emoji_map(emoji_font)
+ return emoji_fonts[0]
+
+def check_emoji_font_coverage(emoji_font, all_emoji, equivalent_emoji):
+ coverage = get_emoji_map(emoji_font)
for sequence in all_emoji:
assert sequence in coverage, (
'%s is not supported in the emoji font.' % printable(sequence))
+ # disable temporarily - we cover more than this
+ """
for sequence in coverage:
if sequence in {0x0000, 0x000D, 0x0020}:
# The font needs to support a few extra characters, which is OK
continue
assert sequence in all_emoji, (
'Emoji font should not support %s.' % printable(sequence))
+ """
for first, second in sorted(equivalent_emoji.items()):
assert coverage[first] == coverage[second], (
@@ -280,6 +290,8 @@
printable(first),
printable(second)))
+ # disable temporarily - some equivalent sequences we don't even know about
+ """
for glyph in set(coverage.values()):
maps_to_glyph = [seq for seq in coverage if coverage[seq] == glyph]
if len(maps_to_glyph) > 1:
@@ -295,7 +307,7 @@
'The sequences %s should not result in the same glyph %s' % (
printable(equivalent_seqs),
glyph))
-
+ """
def check_emoji_defaults(default_emoji):
missing_text_chars = _emoji_properties['Emoji'] - default_emoji
@@ -427,6 +439,11 @@
_emoji_sequences = dict(
(t, v) for (t, v) in _emoji_sequences.items() if not contains_excluded(t))
+ # add in UN flag
+ UN_seq = flag_sequence('UN')
+ _emoji_sequences[UN_seq] = 'Emoji_Flag_Sequence'
+
+
def flag_sequence(territory_code):
return tuple(0x1F1E6 + ord(ch) - ord('A') for ch in territory_code)
@@ -483,6 +500,11 @@
(0x1F468, 0x200D, 0x1F469, 0x200D, 0x1F466): 0x1F46A,
}
+
+def is_fitzpatrick_modifier(cp):
+ return 0x1f3fb <= cp <= 0x1f3ff
+
+
def compute_expected_emoji():
equivalent_emoji = {}
sequence_pieces = set()
@@ -500,7 +522,15 @@
sequence_pieces.update(sequence)
# Add reverse of all emoji ZWJ sequences, which are added to the fonts
# as a workaround to get the sequences work in RTL text.
- reversed_seq = tuple(reversed(sequence))
+ reversed_seq = list(reversed(sequence))
+ # if there are fitzpatrick modifiers in the sequence, keep them after
+ # the emoji they modify
+ for i in xrange(1, len(reversed_seq)):
+ if is_fitzpatrick_modifier(reversed_seq[i - 1]):
+ tmp = reversed_seq[i]
+ reversed_seq[i] = reversed_seq[i-1]
+ reversed_seq[i-1] = tmp
+ reversed_seq = tuple(reversed_seq)
all_sequences.add(reversed_seq)
equivalent_emoji[reversed_seq] = sequence
@@ -536,8 +566,8 @@
def main():
- target_out = sys.argv[1]
global _fonts_dir
+ target_out = sys.argv[1]
_fonts_dir = path.join(target_out, 'fonts')
fonts_xml_path = path.join(target_out, 'etc', 'fonts.xml')
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 04a59bc..58df301 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -78,7 +78,8 @@
@Override
public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, int arg4,
boolean arg5, boolean arg6, int arg7, int arg8, boolean arg9, boolean arg10,
- Rect arg11, Configuration arg12, int arg13, boolean arg14, boolean arg15, int arg16)
+ Rect arg11, Configuration arg12, int arg13, boolean arg14, boolean arg15, int arg16,
+ int arg17)
throws RemoteException {
// TODO Auto-generated method stub
}
@@ -350,7 +351,13 @@
}
@Override
- public void notifyAppStopped(IBinder token, boolean stopped) throws RemoteException {
+ public void notifyAppResumed(IBinder token, boolean wasStopped, boolean allowSavedSurface)
+ throws RemoteException {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
+ public void notifyAppStopped(IBinder token) throws RemoteException {
// TODO Auto-generated method stub
}
@@ -589,4 +596,9 @@
@Override
public void removeWallpaperInputConsumer() throws RemoteException {}
+
+ @Override
+ public Bitmap screenshotWallpaper() throws RemoteException {
+ return null;
+ }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
index 3f276c9..ab73a8b 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
@@ -16,11 +16,13 @@
package com.android.layoutlib.bridge.android;
+import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.InputBindResult;
+import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -239,4 +241,11 @@
// TODO Auto-generated method stub
return null;
}
+
+ @Override
+ public IInputContentUriToken createInputContentUriToken(IBinder token, Uri contentUri,
+ String packageName) {
+ // TODO Auto-generated method stub
+ return null;
+ }
}
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index b614a86..08ad35e 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -375,7 +375,15 @@
return false;
}
+ // wpa_supplicant can update the anonymous identity for these kinds of networks after
+ // framework reads them, so make sure the framework doesn't try to overwrite them.
+ boolean shouldNotWriteAnonIdentity = mEapMethod == WifiEnterpriseConfig.Eap.SIM
+ || mEapMethod == WifiEnterpriseConfig.Eap.AKA
+ || mEapMethod == WifiEnterpriseConfig.Eap.AKA_PRIME;
for (String key : mFields.keySet()) {
+ if (shouldNotWriteAnonIdentity && ANON_IDENTITY_KEY.equals(key)) {
+ continue;
+ }
if (!saver.saveValue(key, mFields.get(key))) {
return false;
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 1cd32b6..bbc3d2f 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -561,6 +561,12 @@
public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
/**
+ * Internally used Wi-Fi lock mode representing the case were no locks are held.
+ * @hide
+ */
+ public static final int WIFI_MODE_NO_LOCKS_HELD = 0;
+
+ /**
* In this Wi-Fi lock mode, Wi-Fi will be kept active,
* and will behave normally, i.e., it will attempt to automatically
* establish a connection to a remembered access point that is
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 2636c3f..716f1d3 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -656,6 +656,40 @@
void onPnoNetworkFound(ScanResult[] results);
}
+ /**
+ * Register a listener that will receive results from all single scans
+ * Either the onSuccess/onFailure will be called once when the listener is registered. After
+ * (assuming onSuccess was called) all subsequent single scan results will be delivered to the
+ * listener. It is possible that onFullResult will not be called for all results of the first
+ * scan if the listener was registered during the scan.
+ *
+ * @param listener specifies the object to report events to. This object is also treated as a
+ * key for this request, and must also be specified to cancel the request.
+ * Multiple requests should also not share this object.
+ * {@hide}
+ */
+ public void registerScanListener(ScanListener listener) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ int key = addListener(listener);
+ if (key == INVALID_KEY) return;
+ validateChannel();
+ mAsyncChannel.sendMessage(CMD_REGISTER_SCAN_LISTENER, 0, key);
+ }
+
+ /**
+ * Deregister a listener for ongoing single scans
+ * @param listener specifies which scan to cancel; must be same object as passed in {@link
+ * #registerScanListener}
+ * {@hide}
+ */
+ public void deregisterScanListener(ScanListener listener) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ int key = removeListener(listener);
+ if (key == INVALID_KEY) return;
+ validateChannel();
+ mAsyncChannel.sendMessage(CMD_DEREGISTER_SCAN_LISTENER, 0, key);
+ }
+
/** start wifi scan in background
* @param settings specifies various parameters for the scan; for more information look at
* {@link ScanSettings}
@@ -1129,6 +1163,10 @@
public static final int CMD_STOP_PNO_SCAN = BASE + 25;
/** @hide */
public static final int CMD_PNO_NETWORK_FOUND = BASE + 26;
+ /** @hide */
+ public static final int CMD_REGISTER_SCAN_LISTENER = BASE + 27;
+ /** @hide */
+ public static final int CMD_DEREGISTER_SCAN_LISTENER = BASE + 28;
private Context mContext;
private IWifiScanner mService;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
index ca737f9..32673c7 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
@@ -146,7 +146,6 @@
}
if (nameValue[0].equals("persistent")) {
- mOwner = new WifiP2pDevice(sa);
mNetId = Integer.parseInt(nameValue[1]);
continue;
}